@bobfrankston/rmfmail 1.1.174 → 1.1.180
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/mailx.js +30 -2
- package/bin/mailx.js.map +1 -1
- package/bin/mailx.ts +32 -3
- package/client/app.bundle.js +123 -0
- package/client/app.bundle.js.map +2 -2
- package/client/app.js +45 -1
- package/client/app.js.map +1 -1
- package/client/app.ts +40 -1
- package/client/components/message-list.js +20 -1
- package/client/components/message-list.js.map +1 -1
- package/client/components/message-list.ts +21 -1
- package/client/compose/compose.bundle.js +2158 -1779
- package/client/compose/compose.bundle.js.map +4 -4
- package/client/compose/compose.js +5 -1
- package/client/compose/compose.js.map +1 -1
- package/client/compose/compose.ts +6 -1
- package/client/compose/editor.js +85 -0
- package/client/compose/editor.js.map +1 -1
- package/client/compose/editor.ts +79 -0
- package/client/compose/spellcheck-core.js +253 -0
- package/client/compose/spellcheck-core.js.map +1 -0
- package/client/compose/spellcheck-core.ts +242 -0
- package/client/lib/api-client.js +89 -0
- package/client/lib/api-client.js.map +1 -1
- package/client/lib/api-client.ts +81 -0
- package/package.json +5 -5
- package/packages/mailx-service/jsonrpc.js +12 -2
- package/packages/mailx-service/jsonrpc.js.map +1 -1
- package/packages/mailx-service/jsonrpc.ts +12 -2
- /package/packages/mailx-imap/{node_modules.npmglobalize-stash-50120 → node_modules.npmglobalize-stash-19808}/.package-lock.json +0 -0
|
@@ -31,1884 +31,2196 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
31
31
|
mod
|
|
32
32
|
));
|
|
33
33
|
|
|
34
|
-
//
|
|
35
|
-
var
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
addUserDictWord: () => addUserDictWord,
|
|
42
|
-
addUserDictWords: () => addUserDictWords,
|
|
43
|
-
aiTransform: () => aiTransform,
|
|
44
|
-
allowRemoteContent: () => allowRemoteContent,
|
|
45
|
-
autocomplete: () => autocomplete,
|
|
46
|
-
cancelQueuedOutgoing: () => cancelQueuedOutgoing,
|
|
47
|
-
cancelServerSearch: () => cancelServerSearch,
|
|
48
|
-
closeWordEdit: () => closeWordEdit,
|
|
49
|
-
connectEvents: () => connectEvents,
|
|
50
|
-
connectWebSocket: () => connectWebSocket,
|
|
51
|
-
consumePendingMailto: () => consumePendingMailto,
|
|
52
|
-
createCalendarEvent: () => createCalendarEvent,
|
|
53
|
-
createFolder: () => createFolder,
|
|
54
|
-
createTask: () => createTask,
|
|
55
|
-
deleteCalendarEvent: () => deleteCalendarEvent,
|
|
56
|
-
deleteContact: () => deleteContact,
|
|
57
|
-
deleteDraft: () => deleteDraft,
|
|
58
|
-
deleteFolder: () => deleteFolder,
|
|
59
|
-
deleteMessage: () => deleteMessage,
|
|
60
|
-
deleteMessages: () => deleteMessages,
|
|
61
|
-
deleteTask: () => deleteTask,
|
|
62
|
-
drainStoreSync: () => drainStoreSync,
|
|
63
|
-
emptyFolder: () => emptyFolder,
|
|
64
|
-
flagSenderOrDomain: () => flagSenderOrDomain,
|
|
65
|
-
formatJsonc: () => formatJsonc,
|
|
66
|
-
getAccounts: () => getAccounts,
|
|
67
|
-
getAttachment: () => getAttachment,
|
|
68
|
-
getAutocompleteSettings: () => getAutocompleteSettings,
|
|
69
|
-
getCalendarEvents: () => getCalendarEvents,
|
|
70
|
-
getCalendars: () => getCalendars,
|
|
71
|
-
getDeviceAccounts: () => getDeviceAccounts,
|
|
72
|
-
getDiagnostics: () => getDiagnostics,
|
|
73
|
-
getFolders: () => getFolders,
|
|
74
|
-
getMessage: () => getMessage,
|
|
75
|
-
getMessages: () => getMessages,
|
|
76
|
-
getOutboxStatus: () => getOutboxStatus,
|
|
77
|
-
getPrimaryAccount: () => getPrimaryAccount,
|
|
78
|
-
getPriorityLists: () => getPriorityLists,
|
|
79
|
-
getSettings: () => getSettings,
|
|
80
|
-
getSyncPending: () => getSyncPending,
|
|
81
|
-
getTasks: () => getTasks,
|
|
82
|
-
getThreadMessages: () => getThreadMessages,
|
|
83
|
-
getUnifiedInbox: () => getUnifiedInbox,
|
|
84
|
-
getUserDict: () => getUserDict,
|
|
85
|
-
getVersion: () => getVersion,
|
|
86
|
-
hasBccHistoryTo: () => hasBccHistoryTo,
|
|
87
|
-
hasCcHistoryTo: () => hasCcHistoryTo,
|
|
88
|
-
listContacts: () => listContacts,
|
|
89
|
-
listQueuedOutgoing: () => listQueuedOutgoing,
|
|
90
|
-
logClientEvent: () => logClientEvent,
|
|
91
|
-
markAsSpamMessages: () => markAsSpamMessages,
|
|
92
|
-
markFolderRead: () => markFolderRead,
|
|
93
|
-
moveFolderToTrash: () => moveFolderToTrash,
|
|
94
|
-
moveMessage: () => moveMessage,
|
|
95
|
-
moveMessages: () => moveMessages,
|
|
96
|
-
onEvent: () => onEvent,
|
|
97
|
-
onWsEvent: () => onWsEvent,
|
|
98
|
-
openAttachment: () => openAttachment,
|
|
99
|
-
openInTextEditor: () => openInTextEditor,
|
|
100
|
-
openInWord: () => openInWord,
|
|
101
|
-
openLocalPath: () => openLocalPath,
|
|
102
|
-
readConfigHelp: () => readConfigHelp,
|
|
103
|
-
readJsoncFile: () => readJsoncFile,
|
|
104
|
-
reauthGoogleScopes: () => reauthGoogleScopes,
|
|
105
|
-
reauthenticate: () => reauthenticate,
|
|
106
|
-
recordSpamReport: () => recordSpamReport,
|
|
107
|
-
removeUserDictWord: () => removeUserDictWord,
|
|
108
|
-
renameFolder: () => renameFolder,
|
|
109
|
-
repairAccounts: () => repairAccounts,
|
|
110
|
-
restartServer: () => restartServer,
|
|
111
|
-
saveAutocompleteSettings: () => saveAutocompleteSettings,
|
|
112
|
-
saveDraft: () => saveDraft,
|
|
113
|
-
saveSettings: () => saveSettings,
|
|
114
|
-
searchContacts: () => searchContacts,
|
|
115
|
-
searchMessages: () => searchMessages,
|
|
116
|
-
sendMessage: () => sendMessage,
|
|
117
|
-
setPriorityDomain: () => setPriorityDomain,
|
|
118
|
-
setPrioritySender: () => setPrioritySender,
|
|
119
|
-
setupAccount: () => setupAccount,
|
|
120
|
-
showReminderPopup: () => showReminderPopup,
|
|
121
|
-
subscribeStore: () => subscribeStore,
|
|
122
|
-
syncAccount: () => syncAccount,
|
|
123
|
-
triggerSync: () => triggerSync,
|
|
124
|
-
undeleteMessage: () => undeleteMessage,
|
|
125
|
-
unsubscribeOneClick: () => unsubscribeOneClick,
|
|
126
|
-
updateCalendarEvent: () => updateCalendarEvent,
|
|
127
|
-
updateFlags: () => updateFlags,
|
|
128
|
-
updateTask: () => updateTask,
|
|
129
|
-
upsertContact: () => upsertContact,
|
|
130
|
-
writeJsoncFile: () => writeJsoncFile
|
|
34
|
+
// node_modules/is-buffer/index.js
|
|
35
|
+
var require_is_buffer = __commonJS({
|
|
36
|
+
"node_modules/is-buffer/index.js"(exports, module) {
|
|
37
|
+
module.exports = function isBuffer(obj) {
|
|
38
|
+
return obj != null && obj.constructor != null && typeof obj.constructor.isBuffer === "function" && obj.constructor.isBuffer(obj);
|
|
39
|
+
};
|
|
40
|
+
}
|
|
131
41
|
});
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
clearTimeout(entry.timer);
|
|
151
|
-
if (ev.data.ok)
|
|
152
|
-
entry.resolve(ev.data.result);
|
|
153
|
-
else
|
|
154
|
-
entry.reject(new Error(ev.data.error || "parent-relay ipc error"));
|
|
155
|
-
});
|
|
156
|
-
const call = (method, args) => {
|
|
157
|
-
const id = `ipc-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
158
|
-
return new Promise((resolve, reject) => {
|
|
159
|
-
const timer = setTimeout(() => {
|
|
160
|
-
pending.delete(id);
|
|
161
|
-
reject(new Error(`parent-relay timeout: ${method}`));
|
|
162
|
-
}, 12e4);
|
|
163
|
-
pending.set(id, { resolve, reject, timer });
|
|
164
|
-
try {
|
|
165
|
-
window.parent.postMessage({ type: "mailx-ipc", id, method, args }, "*");
|
|
166
|
-
} catch (e) {
|
|
167
|
-
clearTimeout(timer);
|
|
168
|
-
pending.delete(id);
|
|
169
|
-
reject(e);
|
|
170
|
-
}
|
|
171
|
-
});
|
|
172
|
-
};
|
|
173
|
-
return new Proxy({}, {
|
|
174
|
-
get(_t, prop) {
|
|
175
|
-
if (prop === "isApp")
|
|
176
|
-
return true;
|
|
177
|
-
if (prop === "platform")
|
|
178
|
-
return window.parent?.mailxapi?.platform || "webview2";
|
|
179
|
-
if (prop === "onEvent") {
|
|
180
|
-
return (handler) => window.parent?.mailxapi?.onEvent?.(handler);
|
|
42
|
+
|
|
43
|
+
// node_modules/nspell/lib/util/rule-codes.js
|
|
44
|
+
var require_rule_codes = __commonJS({
|
|
45
|
+
"node_modules/nspell/lib/util/rule-codes.js"(exports, module) {
|
|
46
|
+
"use strict";
|
|
47
|
+
module.exports = ruleCodes;
|
|
48
|
+
var NO_CODES = [];
|
|
49
|
+
function ruleCodes(flags, value) {
|
|
50
|
+
var index = 0;
|
|
51
|
+
var result;
|
|
52
|
+
if (!value) return NO_CODES;
|
|
53
|
+
if (flags.FLAG === "long") {
|
|
54
|
+
result = new Array(Math.ceil(value.length / 2));
|
|
55
|
+
while (index < value.length) {
|
|
56
|
+
result[index / 2] = value.slice(index, index + 2);
|
|
57
|
+
index += 2;
|
|
58
|
+
}
|
|
59
|
+
return result;
|
|
181
60
|
}
|
|
182
|
-
return (
|
|
61
|
+
return value.split(flags.FLAG === "num" ? "," : "");
|
|
183
62
|
}
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
function ipc() {
|
|
187
|
-
const inIframe = window.parent && window.parent !== window;
|
|
188
|
-
if (inIframe && window.parent?.mailxapi?.isApp) {
|
|
189
|
-
if (!cachedRelayBridge)
|
|
190
|
-
cachedRelayBridge = buildRelayBridge();
|
|
191
|
-
return cachedRelayBridge;
|
|
192
|
-
}
|
|
193
|
-
const bridge = getIpc();
|
|
194
|
-
if (!bridge)
|
|
195
|
-
throw new Error("IPC bridge not available");
|
|
196
|
-
return bridge;
|
|
197
|
-
}
|
|
198
|
-
function abortMessageListRequests() {
|
|
199
|
-
if (messageListAbort) {
|
|
200
|
-
messageListAbort.abort();
|
|
201
|
-
messageListAbort = null;
|
|
202
63
|
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
function recordSpamReport(accountId, uid, folderId) {
|
|
282
|
-
return ipc().recordSpamReport?.(accountId, uid, folderId);
|
|
283
|
-
}
|
|
284
|
-
function getOutboxStatus() {
|
|
285
|
-
return ipc().getOutboxStatus();
|
|
286
|
-
}
|
|
287
|
-
function listQueuedOutgoing() {
|
|
288
|
-
return ipc().listQueuedOutgoing();
|
|
289
|
-
}
|
|
290
|
-
function cancelQueuedOutgoing(p) {
|
|
291
|
-
return ipc().cancelQueuedOutgoing(p);
|
|
292
|
-
}
|
|
293
|
-
function searchContacts(query) {
|
|
294
|
-
return ipc().searchContacts(query);
|
|
295
|
-
}
|
|
296
|
-
function hasCcHistoryTo(email) {
|
|
297
|
-
return ipc().hasCcHistoryTo(email);
|
|
298
|
-
}
|
|
299
|
-
function hasBccHistoryTo(email) {
|
|
300
|
-
return ipc().hasBccHistoryTo(email);
|
|
301
|
-
}
|
|
302
|
-
function listContacts(query, page = 1, pageSize = 100) {
|
|
303
|
-
return ipc().listContacts(query, page, pageSize);
|
|
304
|
-
}
|
|
305
|
-
function upsertContact(name, email) {
|
|
306
|
-
return ipc().upsertContact(name, email);
|
|
307
|
-
}
|
|
308
|
-
function deleteContact(email) {
|
|
309
|
-
return ipc().deleteContact(email);
|
|
310
|
-
}
|
|
311
|
-
function addPreferredContact(entry) {
|
|
312
|
-
return ipc().addPreferredContact(entry.name, entry.email, entry.source, entry.organization);
|
|
313
|
-
}
|
|
314
|
-
function getPriorityLists() {
|
|
315
|
-
return ipc().getPriorityLists();
|
|
316
|
-
}
|
|
317
|
-
function setPrioritySender(email, value, name) {
|
|
318
|
-
return ipc().setPrioritySender(email, value, name);
|
|
319
|
-
}
|
|
320
|
-
function setPriorityDomain(domain, value) {
|
|
321
|
-
return ipc().setPriorityDomain(domain, value);
|
|
322
|
-
}
|
|
323
|
-
function addToDenylist(email) {
|
|
324
|
-
return ipc().addToDenylist(email);
|
|
325
|
-
}
|
|
326
|
-
function openLocalPath(which) {
|
|
327
|
-
return ipc().openLocalPath(which);
|
|
328
|
-
}
|
|
329
|
-
function openInTextEditor(path) {
|
|
330
|
-
return ipc().openInTextEditor?.(path) ?? Promise.resolve({ ok: false, opener: "none", reason: "no host" });
|
|
331
|
-
}
|
|
332
|
-
function allowRemoteContent(type, value) {
|
|
333
|
-
return ipc().allowRemoteContent(type, value);
|
|
334
|
-
}
|
|
335
|
-
function getUserDict() {
|
|
336
|
-
return ipc().getUserDict?.() ?? Promise.resolve([]);
|
|
337
|
-
}
|
|
338
|
-
function addUserDictWord(word) {
|
|
339
|
-
return ipc().addUserDictWord?.(word) ?? Promise.resolve([]);
|
|
340
|
-
}
|
|
341
|
-
function addUserDictWords(words) {
|
|
342
|
-
return ipc().addUserDictWords?.(words) ?? Promise.resolve([]);
|
|
343
|
-
}
|
|
344
|
-
function removeUserDictWord(word) {
|
|
345
|
-
return ipc().removeUserDictWord?.(word) ?? Promise.resolve([]);
|
|
346
|
-
}
|
|
347
|
-
function flagSenderOrDomain(type, value) {
|
|
348
|
-
return ipc().flagSenderOrDomain?.(type, value) ?? Promise.resolve({ flagged: false });
|
|
349
|
-
}
|
|
350
|
-
function deleteMessage(accountId, uid) {
|
|
351
|
-
return ipc().deleteMessage?.(accountId, uid);
|
|
352
|
-
}
|
|
353
|
-
function deleteMessages(accountId, uids) {
|
|
354
|
-
if (uids.length === 1)
|
|
355
|
-
return deleteMessage(accountId, uids[0]);
|
|
356
|
-
return ipc().deleteMessages?.(accountId, uids);
|
|
357
|
-
}
|
|
358
|
-
function moveMessages(accountId, uids, targetFolderId, targetAccountId) {
|
|
359
|
-
if (uids.length === 1)
|
|
360
|
-
return moveMessage(accountId, uids[0], targetFolderId, targetAccountId);
|
|
361
|
-
return ipc().moveMessages?.(accountId, uids, targetFolderId, targetAccountId);
|
|
362
|
-
}
|
|
363
|
-
function markAsSpamMessages(accountId, uids) {
|
|
364
|
-
return ipc().markAsSpamMessages?.(accountId, uids);
|
|
365
|
-
}
|
|
366
|
-
function undeleteMessage(accountId, uid, folderId) {
|
|
367
|
-
return ipc().undeleteMessage?.(accountId, uid, folderId);
|
|
368
|
-
}
|
|
369
|
-
function moveMessage(accountId, uid, targetFolderId, targetAccountId) {
|
|
370
|
-
return ipc().moveMessage?.(accountId, uid, targetFolderId, targetAccountId);
|
|
371
|
-
}
|
|
372
|
-
function restartServer() {
|
|
373
|
-
return ipc().restart?.();
|
|
374
|
-
}
|
|
375
|
-
function markFolderRead(accountId, folderId) {
|
|
376
|
-
return ipc().markFolderRead?.(accountId, folderId);
|
|
377
|
-
}
|
|
378
|
-
function createFolder(accountId, parentPath, name) {
|
|
379
|
-
return ipc().createFolder?.(accountId, parentPath, name);
|
|
380
|
-
}
|
|
381
|
-
function renameFolder(accountId, folderId, newName) {
|
|
382
|
-
return ipc().renameFolder?.(accountId, folderId, newName);
|
|
383
|
-
}
|
|
384
|
-
function deleteFolder(accountId, folderId) {
|
|
385
|
-
return ipc().deleteFolder?.(accountId, folderId);
|
|
386
|
-
}
|
|
387
|
-
function moveFolderToTrash(accountId, folderId) {
|
|
388
|
-
return ipc().moveFolderToTrash?.(accountId, folderId);
|
|
389
|
-
}
|
|
390
|
-
function emptyFolder(accountId, folderId) {
|
|
391
|
-
return ipc().emptyFolder?.(accountId, folderId);
|
|
392
|
-
}
|
|
393
|
-
function logClientEvent(tag, data) {
|
|
394
|
-
let delivered = false;
|
|
395
|
-
try {
|
|
396
|
-
const bridge = typeof globalThis.mailxapi !== "undefined" && globalThis.mailxapi?.isApp ? globalThis.mailxapi : window.opener?.mailxapi?.isApp ? window.opener.mailxapi : window.parent?.mailxapi?.isApp ? window.parent.mailxapi : null;
|
|
397
|
-
if (bridge?.logClientEvent) {
|
|
398
|
-
bridge.logClientEvent(tag, data);
|
|
399
|
-
delivered = true;
|
|
400
|
-
}
|
|
401
|
-
} catch {
|
|
402
|
-
}
|
|
403
|
-
try {
|
|
404
|
-
if (window.parent && window.parent !== window) {
|
|
405
|
-
window.parent.postMessage({ type: "mailx-trace", tag, data, bridged: delivered }, "*");
|
|
406
|
-
}
|
|
407
|
-
} catch {
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
function sendMessage(body) {
|
|
411
|
-
return ipc().sendMessage?.(body);
|
|
412
|
-
}
|
|
413
|
-
function saveDraft(body) {
|
|
414
|
-
return ipc().saveDraft?.(body);
|
|
415
|
-
}
|
|
416
|
-
function onEvent(handler) {
|
|
417
|
-
eventHandlers.push(handler);
|
|
418
|
-
return () => {
|
|
419
|
-
const i = eventHandlers.indexOf(handler);
|
|
420
|
-
if (i >= 0)
|
|
421
|
-
eventHandlers.splice(i, 1);
|
|
422
|
-
};
|
|
423
|
-
}
|
|
424
|
-
function subscribeStore(topic, handler) {
|
|
425
|
-
let set = storeSubs.get(topic);
|
|
426
|
-
if (!set) {
|
|
427
|
-
set = /* @__PURE__ */ new Set();
|
|
428
|
-
storeSubs.set(topic, set);
|
|
429
|
-
}
|
|
430
|
-
set.add(handler);
|
|
431
|
-
return () => {
|
|
432
|
-
const s = storeSubs.get(topic);
|
|
433
|
-
if (s) {
|
|
434
|
-
s.delete(handler);
|
|
435
|
-
if (s.size === 0)
|
|
436
|
-
storeSubs.delete(topic);
|
|
437
|
-
}
|
|
438
|
-
};
|
|
439
|
-
}
|
|
440
|
-
function deliverStore(event) {
|
|
441
|
-
const exact = storeSubs.get(event.topic);
|
|
442
|
-
if (exact)
|
|
443
|
-
for (const h of exact) {
|
|
444
|
-
try {
|
|
445
|
-
h(event);
|
|
446
|
-
} catch (e) {
|
|
447
|
-
console.error("[store-bus]", e);
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
const wild = storeSubs.get("*");
|
|
451
|
-
if (wild)
|
|
452
|
-
for (const h of wild) {
|
|
453
|
-
try {
|
|
454
|
-
h(event);
|
|
455
|
-
} catch (e) {
|
|
456
|
-
console.error("[store-bus]", e);
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
function connectEvents() {
|
|
461
|
-
ipc().onEvent((event) => {
|
|
462
|
-
if (event && event._event === "store")
|
|
463
|
-
deliverStore(event);
|
|
464
|
-
for (const h of eventHandlers)
|
|
465
|
-
h(event);
|
|
466
|
-
});
|
|
467
|
-
}
|
|
468
|
-
function autocomplete(body, signal) {
|
|
469
|
-
return ipc().autocomplete?.(body);
|
|
470
|
-
}
|
|
471
|
-
function getAutocompleteSettings() {
|
|
472
|
-
return ipc().getAutocompleteSettings?.();
|
|
473
|
-
}
|
|
474
|
-
function saveAutocompleteSettings(settings) {
|
|
475
|
-
return ipc().saveAutocompleteSettings?.(settings);
|
|
476
|
-
}
|
|
477
|
-
function getVersion() {
|
|
478
|
-
return ipc().getVersion();
|
|
479
|
-
}
|
|
480
|
-
function getSettings() {
|
|
481
|
-
return ipc().getSettings();
|
|
482
|
-
}
|
|
483
|
-
function saveSettings(settings) {
|
|
484
|
-
return ipc().saveSettingsData?.(settings);
|
|
485
|
-
}
|
|
486
|
-
function repairAccounts() {
|
|
487
|
-
return ipc().repairAccounts?.();
|
|
488
|
-
}
|
|
489
|
-
function deleteDraft(accountId, draftUid2, draftId2) {
|
|
490
|
-
return ipc().deleteDraft?.(accountId, draftUid2, draftId2);
|
|
491
|
-
}
|
|
492
|
-
function addContact(name, email) {
|
|
493
|
-
return ipc().addContact?.(name, email);
|
|
494
|
-
}
|
|
495
|
-
function getThreadMessages(accountId, threadId) {
|
|
496
|
-
return ipc().getThreadMessages?.(accountId, threadId);
|
|
497
|
-
}
|
|
498
|
-
function readJsoncFile(name) {
|
|
499
|
-
return ipc().readJsoncFile?.(name);
|
|
500
|
-
}
|
|
501
|
-
function writeJsoncFile(name, content) {
|
|
502
|
-
return ipc().writeJsoncFile?.(name, content);
|
|
503
|
-
}
|
|
504
|
-
function formatJsonc(content) {
|
|
505
|
-
return ipc().formatJsonc?.(content);
|
|
506
|
-
}
|
|
507
|
-
function readConfigHelp(name) {
|
|
508
|
-
return ipc().readConfigHelp?.(name) ?? Promise.resolve({ content: "" });
|
|
509
|
-
}
|
|
510
|
-
function unsubscribeOneClick(url) {
|
|
511
|
-
return ipc().unsubscribeOneClick?.(url);
|
|
512
|
-
}
|
|
513
|
-
function openInWord(editId, html) {
|
|
514
|
-
return ipc().openInWord?.(editId, html) ?? Promise.resolve({ ok: false, path: "", opener: "none" });
|
|
515
|
-
}
|
|
516
|
-
function closeWordEdit(editId) {
|
|
517
|
-
return ipc().closeWordEdit?.(editId) ?? Promise.resolve();
|
|
518
|
-
}
|
|
519
|
-
function showReminderPopup(opts) {
|
|
520
|
-
return ipc().showReminderPopup?.(opts) ?? Promise.resolve({ button: "", reason: "no host" });
|
|
521
|
-
}
|
|
522
|
-
function consumePendingMailto() {
|
|
523
|
-
return ipc().consumePendingMailto?.() ?? Promise.resolve(null);
|
|
524
|
-
}
|
|
525
|
-
function aiTransform(req) {
|
|
526
|
-
return ipc().aiTransform?.(req) ?? Promise.resolve({ text: "", reason: "AI not available in this host" });
|
|
527
|
-
}
|
|
528
|
-
function setupAccount(name, email, password) {
|
|
529
|
-
return ipc().setupAccount?.(name, email, password);
|
|
530
|
-
}
|
|
531
|
-
async function getAttachment(accountId, uid, attachmentId, folderId) {
|
|
532
|
-
return ipc().getAttachment(accountId, uid, attachmentId, folderId);
|
|
533
|
-
}
|
|
534
|
-
async function openAttachment(accountId, uid, attachmentId, folderId) {
|
|
535
|
-
const fn = ipc().openAttachment;
|
|
536
|
-
return fn ? fn(accountId, uid, attachmentId, folderId) : void 0;
|
|
537
|
-
}
|
|
538
|
-
async function getDeviceAccounts() {
|
|
539
|
-
return ipc().getDeviceAccounts?.() ?? [];
|
|
540
|
-
}
|
|
541
|
-
var cachedRelayBridge, messageListAbort, eventHandlers, storeSubs, connectWebSocket, onWsEvent;
|
|
542
|
-
var init_api_client = __esm({
|
|
543
|
-
"client/lib/api-client.js"() {
|
|
544
|
-
"use strict";
|
|
545
|
-
cachedRelayBridge = null;
|
|
546
|
-
messageListAbort = null;
|
|
547
|
-
eventHandlers = [];
|
|
548
|
-
storeSubs = /* @__PURE__ */ new Map();
|
|
549
|
-
connectWebSocket = connectEvents;
|
|
550
|
-
onWsEvent = onEvent;
|
|
551
|
-
}
|
|
552
|
-
});
|
|
553
|
-
|
|
554
|
-
// client/lib/rmf-tiny.js
|
|
555
|
-
var rmf_tiny_exports = {};
|
|
556
|
-
__export(rmf_tiny_exports, {
|
|
557
|
-
createTinyMceEditor: () => createTinyMceEditor
|
|
558
|
-
});
|
|
559
|
-
async function loadTinymce(opts) {
|
|
560
|
-
try {
|
|
561
|
-
const mod = await import(
|
|
562
|
-
/* @vite-ignore */
|
|
563
|
-
"tinymce"
|
|
564
|
-
);
|
|
565
|
-
const tinymce = mod.default || mod;
|
|
566
|
-
await Promise.all([
|
|
567
|
-
import("tinymce/themes/silver").catch(() => {
|
|
568
|
-
}),
|
|
569
|
-
import("tinymce/icons/default").catch(() => {
|
|
570
|
-
}),
|
|
571
|
-
import("tinymce/models/dom").catch(() => {
|
|
572
|
-
}),
|
|
573
|
-
import("tinymce/plugins/paste").catch(() => {
|
|
574
|
-
}),
|
|
575
|
-
import("tinymce/plugins/lists").catch(() => {
|
|
576
|
-
}),
|
|
577
|
-
import("tinymce/plugins/link").catch(() => {
|
|
578
|
-
}),
|
|
579
|
-
import("tinymce/plugins/table").catch(() => {
|
|
580
|
-
}),
|
|
581
|
-
import("tinymce/plugins/code").catch(() => {
|
|
582
|
-
}),
|
|
583
|
-
import("tinymce/plugins/image").catch(() => {
|
|
584
|
-
})
|
|
585
|
-
]);
|
|
586
|
-
return tinymce;
|
|
587
|
-
} catch {
|
|
588
|
-
}
|
|
589
|
-
const w = window;
|
|
590
|
-
if (w.tinymce)
|
|
591
|
-
return w.tinymce;
|
|
592
|
-
if (!opts.cdnUrl) {
|
|
593
|
-
throw new Error("rmf-tiny: tinymce not installed (npm install tinymce) and no cdnUrl supplied. See README for Android setup.");
|
|
594
|
-
}
|
|
595
|
-
const url = opts.apiKey && !opts.cdnUrl.includes("api-key") ? `${opts.cdnUrl}${opts.cdnUrl.includes("?") ? "&" : "?"}apiKey=${encodeURIComponent(opts.apiKey)}` : opts.cdnUrl;
|
|
596
|
-
await new Promise((resolve, reject) => {
|
|
597
|
-
const s = document.createElement("script");
|
|
598
|
-
s.src = url;
|
|
599
|
-
s.referrerPolicy = "origin";
|
|
600
|
-
s.onload = () => resolve();
|
|
601
|
-
s.onerror = () => reject(new Error(`rmf-tiny: failed to load TinyMCE from ${url}`));
|
|
602
|
-
document.head.appendChild(s);
|
|
603
|
-
});
|
|
604
|
-
if (!w.tinymce)
|
|
605
|
-
throw new Error("rmf-tiny: TinyMCE script loaded but window.tinymce is missing");
|
|
606
|
-
return w.tinymce;
|
|
607
|
-
}
|
|
608
|
-
async function createTinyMceEditor(container2, opts = {}) {
|
|
609
|
-
const tinymce = await loadTinymce(opts);
|
|
610
|
-
const target = document.createElement("div");
|
|
611
|
-
target.id = `rmf-tiny-${Math.random().toString(36).slice(2, 10)}`;
|
|
612
|
-
target.style.cssText = "width:100%;height:100%;";
|
|
613
|
-
container2.appendChild(target);
|
|
614
|
-
const editor2 = await new Promise((resolve) => {
|
|
615
|
-
tinymce.init({
|
|
616
|
-
target,
|
|
617
|
-
// Word-paste fidelity is the entire point of using TinyMCE here.
|
|
618
|
-
// `paste_as_text: false` keeps formatting; powerpaste isn't in
|
|
619
|
-
// OSS but the standard paste plugin handles Word reasonably well.
|
|
620
|
-
// All free / OSS plugins bundled in the jsDelivr tinymce@6 script.
|
|
621
|
-
// No premium plugins listed — listing one without an API key
|
|
622
|
-
// triggers TinyMCE's upsell dialog on every load.
|
|
623
|
-
plugins: "paste lists advlist link table code codesample image searchreplace autolink wordcount emoticons charmap insertdatetime quickbars nonbreaking directionality help",
|
|
624
|
-
toolbar: [
|
|
625
|
-
"undo redo | bold italic underline strikethrough | forecolor backcolor",
|
|
626
|
-
"bullist numlist outdent indent | link table image code codesample | emoticons charmap | help"
|
|
627
|
-
].join(" | "),
|
|
628
|
-
// Include "tools" so wordcount and searchreplace are reachable.
|
|
629
|
-
menubar: "file edit view insert format tools",
|
|
630
|
-
// View menu — append zoom commands. fullscreen omitted: this editor
|
|
631
|
-
// lives inside a compose iframe overlay that already fills its host
|
|
632
|
-
// window; TinyMCE's fullscreen plugin re-positions the container in
|
|
633
|
-
// ways that produce a blank body, so it's not in the plugin list.
|
|
634
|
-
menu: {
|
|
635
|
-
view: { title: "View", items: "code | visualaid visualchars visualblocks | preview | zoomIn zoomOut zoomReset" }
|
|
636
|
-
},
|
|
637
|
-
// Disable the "Get all features" upsell badge that TinyMCE 6+
|
|
638
|
-
// injects automatically when no premium plugins are listed.
|
|
639
|
-
promotion: false,
|
|
640
|
-
// Floating toolbar that appears on text selection — keeps the
|
|
641
|
-
// main toolbar uncluttered while exposing common formatters
|
|
642
|
-
// where the cursor already is. quickbars_insert_toolbar:false
|
|
643
|
-
// suppresses the second (insert-on-blank-line) popup which
|
|
644
|
-
// clutters more than it helps in an email composer.
|
|
645
|
-
quickbars_selection_toolbar: "bold italic underline | forecolor | quicklink blockquote",
|
|
646
|
-
quickbars_insert_toolbar: false,
|
|
647
|
-
quickbars_image_toolbar: "alignleft aligncenter alignright",
|
|
648
|
-
// Code-sample dropdown languages. TinyMCE's default list omits
|
|
649
|
-
// "Text" / plain — every option triggers syntax highlighting
|
|
650
|
-
// which mangles unrelated paste content (Bob 2026-05-24).
|
|
651
|
-
// Adding Text first so it's the default; rest are the modern
|
|
652
|
-
// languages we actually paste.
|
|
653
|
-
codesample_languages: [
|
|
654
|
-
{ text: "Text", value: "text" },
|
|
655
|
-
{ text: "HTML/XML", value: "markup" },
|
|
656
|
-
{ text: "JavaScript", value: "javascript" },
|
|
657
|
-
{ text: "TypeScript", value: "typescript" },
|
|
658
|
-
{ text: "CSS", value: "css" },
|
|
659
|
-
{ text: "JSON", value: "json" },
|
|
660
|
-
{ text: "Python", value: "python" },
|
|
661
|
-
{ text: "Java", value: "java" },
|
|
662
|
-
{ text: "C", value: "c" },
|
|
663
|
-
{ text: "C++", value: "cpp" },
|
|
664
|
-
{ text: "C#", value: "csharp" },
|
|
665
|
-
{ text: "Go", value: "go" },
|
|
666
|
-
{ text: "Rust", value: "rust" },
|
|
667
|
-
{ text: "Ruby", value: "ruby" },
|
|
668
|
-
{ text: "PHP", value: "php" },
|
|
669
|
-
{ text: "Shell", value: "bash" },
|
|
670
|
-
{ text: "SQL", value: "sql" }
|
|
671
|
-
],
|
|
672
|
-
// WebView's native spell-check (red underlines, right-click
|
|
673
|
-
// "Add to dictionary"). Free; same UX as Quill's spellcheck=true.
|
|
674
|
-
// Premium tinymcespellchecker plugin would replace this with a
|
|
675
|
-
// custom backend via spellchecker_rpc_url, but requires a
|
|
676
|
-
// Tiny Cloud subscription.
|
|
677
|
-
browser_spellcheck: true,
|
|
678
|
-
// Right-click context menu DISABLED so WebView2's native menu
|
|
679
|
-
// fires instead. We need the native menu because that's where
|
|
680
|
-
// the browser shows spelling suggestions (TinyMCE's contextmenu
|
|
681
|
-
// intercepts the right-click and replaces the menu — red
|
|
682
|
-
// underlines appear but suggestions are unreachable). Trade-off:
|
|
683
|
-
// lose TinyMCE's link / image / table quick actions. Standard
|
|
684
|
-
// text formatting is still on the toolbar.
|
|
685
|
-
contextmenu: false,
|
|
686
|
-
statusbar: false,
|
|
687
|
-
branding: false,
|
|
688
|
-
license_key: "gpl",
|
|
689
|
-
paste_data_images: true,
|
|
690
|
-
// Permissive valid_elements — preserve as much of the source
|
|
691
|
-
// formatting as possible. The default is more aggressive about
|
|
692
|
-
// stripping. Empty string for `valid_elements` means accept
|
|
693
|
-
// everything that the schema allows.
|
|
694
|
-
paste_word_valid_elements: "@[style|class],-strong/b,-em/i,-u,-s,-sub,-sup,-strike,-p,-ol,-ul,-li,-h1,-h2,-h3,-h4,-h5,-h6,-blockquote,-table[border|cellpadding|cellspacing|width|height|class|style],-tr,-td[colspan|rowspan|width|height|class|style|valign|align|background|bgcolor],-th,-thead,-tbody,-tfoot,-pre,-br,-a[href|target|title],-img[src|alt|width|height|style|class]",
|
|
695
|
-
paste_retain_style_properties: "color background background-color font-family font-size font-weight font-style text-decoration text-align padding padding-top padding-bottom padding-left padding-right margin margin-top margin-bottom margin-left margin-right border border-top border-bottom border-left border-right",
|
|
696
|
-
// Auto-link bare URLs in pasted content. TinyMCE's `autolink`
|
|
697
|
-
// plugin only fires on TYPED space/enter; URLs that arrive via
|
|
698
|
-
// clipboard (browser address bar, terminal copy) come in as
|
|
699
|
-
// plain text and stay un-linked. paste_preprocess runs on the
|
|
700
|
-
// HTML the paste plugin produced.
|
|
701
|
-
//
|
|
702
|
-
// CRITICAL: skip auto-link when the content already contains
|
|
703
|
-
// anchors. Naive regex over the whole content would wrap
|
|
704
|
-
// `<a href="X">X</a>` in ANOTHER anchor (nested anchors are
|
|
705
|
-
// invalid HTML and browsers split them, producing visible
|
|
706
|
-
// junk). For HTML pastes that already have linked URLs (the
|
|
707
|
-
// common case from a browser address bar or another mail
|
|
708
|
-
// client), TinyMCE preserves them — no auto-link pass needed.
|
|
709
|
-
// For plain-text pastes (no anchors present), wrap any bare
|
|
710
|
-
// http(s)://… runs. Trailing sentence punctuation is excluded
|
|
711
|
-
// from the URL.
|
|
712
|
-
paste_preprocess: (_plugin, args) => {
|
|
713
|
-
if (/<a[\s>]/i.test(args.content))
|
|
714
|
-
return;
|
|
715
|
-
args.content = args.content.replace(/(^|[\s(\[])((?:https?|ftp):\/\/[^\s<>"']+[^\s<>"'.,;:!?)\]])/gi, (_m, lead, url) => `${lead}<a href="${url}">${url}</a>`);
|
|
716
|
-
},
|
|
717
|
-
// Body font + quoted-reply styling. Without explicit rules for
|
|
718
|
-
// <blockquote> and div.reply the editor iframe renders the
|
|
719
|
-
// quoted block with no visual distinction from the user's own
|
|
720
|
-
// text — pasted reply quotes vanish into a wall of unformatted
|
|
721
|
-
// paragraphs (Bob 2026-05-12: "the body losing all formatting").
|
|
722
|
-
// The styles below match Thunderbird/Outlook conventions: a
|
|
723
|
-
// muted left-bar + indent on blockquotes, slight color shift on
|
|
724
|
-
// the "On … wrote:" attribution, untouched typography for
|
|
725
|
-
// everything else so genuine HTML formatting (bold / italic /
|
|
726
|
-
// tables / inline color) still comes through verbatim.
|
|
727
|
-
content_style: [
|
|
728
|
-
// Dark-blue native caret bar. caret-shape:block produced a
|
|
729
|
-
// column wider than the gap between letters AND caused the
|
|
730
|
-
// caret to "scoot back to its old position" after an arrow
|
|
731
|
-
// press (Chromium block-caret quirk with the arrow-key
|
|
732
|
-
// selection adjustment, Bob 2026-05-24). Plain bar is
|
|
733
|
-
// browser-default and behaves correctly with arrow keys;
|
|
734
|
-
// just darken the color so it stays visible.
|
|
735
|
-
"body { font-family: system-ui, sans-serif; font-size: 14px; caret-color: #0a2647; }",
|
|
736
|
-
"blockquote { border-left: 3px solid #c0c8d0; margin: 0 0 0 4px; padding: 2px 0 2px 10px; color: #555; }",
|
|
737
|
-
"div.reply { margin-top: 0.5em; }",
|
|
738
|
-
"div.reply > p:first-child { color: #666; font-size: 0.95em; margin: 0 0 4px 0; }",
|
|
739
|
-
"pre, code { font-family: ui-monospace, Consolas, Menlo, monospace; }"
|
|
740
|
-
].join(" "),
|
|
741
|
-
init_instance_callback: (ed) => resolve(ed),
|
|
742
|
-
setup: (ed) => {
|
|
743
|
-
if (opts.initialHtml)
|
|
744
|
-
ed.on("init", () => ed.setContent(opts.initialHtml));
|
|
745
|
-
const ZOOM_DEFAULT = 14;
|
|
746
|
-
const ZOOM_STEP = 2;
|
|
747
|
-
const ZOOM_MIN = 8;
|
|
748
|
-
const ZOOM_MAX = 32;
|
|
749
|
-
const ZOOM_STORAGE_KEY = "rmf-tiny:zoom-px";
|
|
750
|
-
let zoomPx = ZOOM_DEFAULT;
|
|
751
|
-
try {
|
|
752
|
-
const stored = Number(localStorage.getItem(ZOOM_STORAGE_KEY));
|
|
753
|
-
if (Number.isFinite(stored) && stored >= ZOOM_MIN && stored <= ZOOM_MAX) {
|
|
754
|
-
zoomPx = stored;
|
|
755
|
-
}
|
|
756
|
-
} catch {
|
|
757
|
-
}
|
|
758
|
-
const saveZoom = () => {
|
|
759
|
-
try {
|
|
760
|
-
localStorage.setItem(ZOOM_STORAGE_KEY, String(zoomPx));
|
|
761
|
-
} catch {
|
|
762
|
-
}
|
|
763
|
-
};
|
|
764
|
-
const applyZoom = () => {
|
|
765
|
-
try {
|
|
766
|
-
const body = ed.getBody();
|
|
767
|
-
if (body)
|
|
768
|
-
body.style.fontSize = `${zoomPx}px`;
|
|
769
|
-
} catch {
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// node_modules/nspell/lib/util/affix.js
|
|
67
|
+
var require_affix = __commonJS({
|
|
68
|
+
"node_modules/nspell/lib/util/affix.js"(exports, module) {
|
|
69
|
+
"use strict";
|
|
70
|
+
var parse = require_rule_codes();
|
|
71
|
+
module.exports = affix;
|
|
72
|
+
var push = [].push;
|
|
73
|
+
var alphabet = "etaoinshrdlcumwfgypbvkjxqz".split("");
|
|
74
|
+
var whiteSpaceExpression = /\s+/;
|
|
75
|
+
var defaultKeyboardLayout = [
|
|
76
|
+
"qwertzuop",
|
|
77
|
+
"yxcvbnm",
|
|
78
|
+
"qaw",
|
|
79
|
+
"say",
|
|
80
|
+
"wse",
|
|
81
|
+
"dsx",
|
|
82
|
+
"sy",
|
|
83
|
+
"edr",
|
|
84
|
+
"fdc",
|
|
85
|
+
"dx",
|
|
86
|
+
"rft",
|
|
87
|
+
"gfv",
|
|
88
|
+
"fc",
|
|
89
|
+
"tgz",
|
|
90
|
+
"hgb",
|
|
91
|
+
"gv",
|
|
92
|
+
"zhu",
|
|
93
|
+
"jhn",
|
|
94
|
+
"hb",
|
|
95
|
+
"uji",
|
|
96
|
+
"kjm",
|
|
97
|
+
"jn",
|
|
98
|
+
"iko",
|
|
99
|
+
"lkm"
|
|
100
|
+
];
|
|
101
|
+
function affix(doc) {
|
|
102
|
+
var rules = /* @__PURE__ */ Object.create(null);
|
|
103
|
+
var compoundRuleCodes = /* @__PURE__ */ Object.create(null);
|
|
104
|
+
var flags = /* @__PURE__ */ Object.create(null);
|
|
105
|
+
var replacementTable = [];
|
|
106
|
+
var conversion = { in: [], out: [] };
|
|
107
|
+
var compoundRules = [];
|
|
108
|
+
var aff = doc.toString("utf8");
|
|
109
|
+
var lines = [];
|
|
110
|
+
var last = 0;
|
|
111
|
+
var index = aff.indexOf("\n");
|
|
112
|
+
var parts;
|
|
113
|
+
var line;
|
|
114
|
+
var ruleType;
|
|
115
|
+
var count;
|
|
116
|
+
var remove;
|
|
117
|
+
var add;
|
|
118
|
+
var source;
|
|
119
|
+
var entry;
|
|
120
|
+
var position;
|
|
121
|
+
var rule;
|
|
122
|
+
var value;
|
|
123
|
+
var offset;
|
|
124
|
+
var character;
|
|
125
|
+
flags.KEY = [];
|
|
126
|
+
while (index > -1) {
|
|
127
|
+
pushLine(aff.slice(last, index));
|
|
128
|
+
last = index + 1;
|
|
129
|
+
index = aff.indexOf("\n", last);
|
|
130
|
+
}
|
|
131
|
+
pushLine(aff.slice(last));
|
|
132
|
+
index = -1;
|
|
133
|
+
while (++index < lines.length) {
|
|
134
|
+
line = lines[index];
|
|
135
|
+
parts = line.split(whiteSpaceExpression);
|
|
136
|
+
ruleType = parts[0];
|
|
137
|
+
if (ruleType === "REP") {
|
|
138
|
+
count = index + parseInt(parts[1], 10);
|
|
139
|
+
while (++index <= count) {
|
|
140
|
+
parts = lines[index].split(whiteSpaceExpression);
|
|
141
|
+
replacementTable.push([parts[1], parts[2]]);
|
|
770
142
|
}
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
text: "Zoom in",
|
|
779
|
-
shortcut: "Ctrl+=",
|
|
780
|
-
onAction: () => bumpZoom(ZOOM_STEP)
|
|
781
|
-
});
|
|
782
|
-
ed.ui.registry.addMenuItem("zoomOut", {
|
|
783
|
-
text: "Zoom out",
|
|
784
|
-
shortcut: "Ctrl+-",
|
|
785
|
-
onAction: () => bumpZoom(-ZOOM_STEP)
|
|
786
|
-
});
|
|
787
|
-
ed.ui.registry.addMenuItem("zoomReset", {
|
|
788
|
-
text: "Reset zoom",
|
|
789
|
-
shortcut: "Ctrl+0",
|
|
790
|
-
onAction: () => {
|
|
791
|
-
zoomPx = ZOOM_DEFAULT;
|
|
792
|
-
applyZoom();
|
|
793
|
-
saveZoom();
|
|
143
|
+
index--;
|
|
144
|
+
} else if (ruleType === "ICONV" || ruleType === "OCONV") {
|
|
145
|
+
count = index + parseInt(parts[1], 10);
|
|
146
|
+
entry = conversion[ruleType === "ICONV" ? "in" : "out"];
|
|
147
|
+
while (++index <= count) {
|
|
148
|
+
parts = lines[index].split(whiteSpaceExpression);
|
|
149
|
+
entry.push([new RegExp(parts[1], "g"), parts[2]]);
|
|
794
150
|
}
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
151
|
+
index--;
|
|
152
|
+
} else if (ruleType === "COMPOUNDRULE") {
|
|
153
|
+
count = index + parseInt(parts[1], 10);
|
|
154
|
+
while (++index <= count) {
|
|
155
|
+
rule = lines[index].split(whiteSpaceExpression)[1];
|
|
156
|
+
position = -1;
|
|
157
|
+
compoundRules.push(rule);
|
|
158
|
+
while (++position < rule.length) {
|
|
159
|
+
compoundRuleCodes[rule.charAt(position)] = [];
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
index--;
|
|
163
|
+
} else if (ruleType === "PFX" || ruleType === "SFX") {
|
|
164
|
+
count = index + parseInt(parts[3], 10);
|
|
165
|
+
rule = {
|
|
166
|
+
type: ruleType,
|
|
167
|
+
combineable: parts[2] === "Y",
|
|
168
|
+
entries: []
|
|
169
|
+
};
|
|
170
|
+
rules[parts[1]] = rule;
|
|
171
|
+
while (++index <= count) {
|
|
172
|
+
parts = lines[index].split(whiteSpaceExpression);
|
|
173
|
+
remove = parts[2];
|
|
174
|
+
add = parts[3].split("/");
|
|
175
|
+
source = parts[4];
|
|
176
|
+
entry = {
|
|
177
|
+
add: "",
|
|
178
|
+
remove: "",
|
|
179
|
+
match: "",
|
|
180
|
+
continuation: parse(flags, add[1])
|
|
181
|
+
};
|
|
182
|
+
if (add && add[0] !== "0") {
|
|
183
|
+
entry.add = add[0];
|
|
184
|
+
}
|
|
820
185
|
try {
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
186
|
+
if (remove !== "0") {
|
|
187
|
+
entry.remove = ruleType === "SFX" ? end(remove) : remove;
|
|
188
|
+
}
|
|
189
|
+
if (source && source !== ".") {
|
|
190
|
+
entry.match = ruleType === "SFX" ? end(source) : start(source);
|
|
191
|
+
}
|
|
192
|
+
} catch (_) {
|
|
193
|
+
entry = null;
|
|
825
194
|
}
|
|
826
|
-
if (
|
|
827
|
-
|
|
828
|
-
const after = doc.createRange();
|
|
829
|
-
after.setStartAfter(link);
|
|
830
|
-
after.collapse(true);
|
|
831
|
-
sel.removeAllRanges();
|
|
832
|
-
sel.addRange(after);
|
|
833
|
-
}, true);
|
|
834
|
-
};
|
|
835
|
-
ed.on("init", installLinkEscape);
|
|
836
|
-
ed.on("SetContent", installLinkEscape);
|
|
837
|
-
ed.on("init", () => {
|
|
838
|
-
try {
|
|
839
|
-
const body = ed.getBody();
|
|
840
|
-
if (body) {
|
|
841
|
-
body.setAttribute("spellcheck", "true");
|
|
842
|
-
body.setAttribute("lang", "en");
|
|
195
|
+
if (entry) {
|
|
196
|
+
rule.entries.push(entry);
|
|
843
197
|
}
|
|
844
|
-
const doc = ed.getDoc();
|
|
845
|
-
if (doc?.documentElement)
|
|
846
|
-
doc.documentElement.setAttribute("lang", "en");
|
|
847
|
-
} catch {
|
|
848
198
|
}
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
}
|
|
859
|
-
doc.addEventListener("keydown", (e) => {
|
|
860
|
-
if (!(e.ctrlKey || e.metaKey))
|
|
861
|
-
return;
|
|
862
|
-
if (e.key === "=" || e.key === "+") {
|
|
863
|
-
e.preventDefault();
|
|
864
|
-
e.stopPropagation();
|
|
865
|
-
bumpZoom(ZOOM_STEP);
|
|
866
|
-
} else if (e.key === "-") {
|
|
867
|
-
e.preventDefault();
|
|
868
|
-
e.stopPropagation();
|
|
869
|
-
bumpZoom(-ZOOM_STEP);
|
|
870
|
-
} else if (e.key === "0") {
|
|
871
|
-
e.preventDefault();
|
|
872
|
-
e.stopPropagation();
|
|
873
|
-
zoomPx = ZOOM_DEFAULT;
|
|
874
|
-
applyZoom();
|
|
875
|
-
}
|
|
876
|
-
}, true);
|
|
877
|
-
} catch {
|
|
199
|
+
index--;
|
|
200
|
+
} else if (ruleType === "TRY") {
|
|
201
|
+
source = parts[1];
|
|
202
|
+
offset = -1;
|
|
203
|
+
value = [];
|
|
204
|
+
while (++offset < source.length) {
|
|
205
|
+
character = source.charAt(offset);
|
|
206
|
+
if (character.toLowerCase() === character) {
|
|
207
|
+
value.push(character);
|
|
208
|
+
}
|
|
878
209
|
}
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
setHtml(html) {
|
|
885
|
-
editor2.setContent(html);
|
|
886
|
-
},
|
|
887
|
-
getHtml() {
|
|
888
|
-
return editor2.getContent();
|
|
889
|
-
},
|
|
890
|
-
getText() {
|
|
891
|
-
return editor2.getContent({ format: "text" });
|
|
892
|
-
},
|
|
893
|
-
focus() {
|
|
894
|
-
editor2.focus();
|
|
895
|
-
},
|
|
896
|
-
setCursor(pos) {
|
|
897
|
-
const place = () => {
|
|
898
|
-
const body = editor2.getBody();
|
|
899
|
-
if (pos === 0) {
|
|
900
|
-
const first = body.firstChild;
|
|
901
|
-
if (first && first.nodeType === 1) {
|
|
902
|
-
editor2.selection.setCursorLocation(first, 0);
|
|
903
|
-
} else {
|
|
904
|
-
editor2.selection.select(body, true);
|
|
905
|
-
editor2.selection.collapse(true);
|
|
210
|
+
offset = -1;
|
|
211
|
+
while (++offset < alphabet.length) {
|
|
212
|
+
if (source.indexOf(alphabet[offset]) < 0) {
|
|
213
|
+
value.push(alphabet[offset]);
|
|
214
|
+
}
|
|
906
215
|
}
|
|
907
|
-
|
|
908
|
-
|
|
216
|
+
flags[ruleType] = value;
|
|
217
|
+
} else if (ruleType === "KEY") {
|
|
218
|
+
push.apply(flags[ruleType], parts[1].split("|"));
|
|
219
|
+
} else if (ruleType === "COMPOUNDMIN") {
|
|
220
|
+
flags[ruleType] = Number(parts[1]);
|
|
221
|
+
} else if (ruleType === "ONLYINCOMPOUND") {
|
|
222
|
+
flags[ruleType] = parts[1];
|
|
223
|
+
compoundRuleCodes[parts[1]] = [];
|
|
224
|
+
} else if (ruleType === "FLAG" || ruleType === "KEEPCASE" || ruleType === "NOSUGGEST" || ruleType === "WORDCHARS") {
|
|
225
|
+
flags[ruleType] = parts[1];
|
|
909
226
|
} else {
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
227
|
+
flags[ruleType] = parts[1];
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
if (isNaN(flags.COMPOUNDMIN)) {
|
|
231
|
+
flags.COMPOUNDMIN = 3;
|
|
232
|
+
}
|
|
233
|
+
if (!flags.KEY.length) {
|
|
234
|
+
flags.KEY = defaultKeyboardLayout;
|
|
235
|
+
}
|
|
236
|
+
if (!flags.TRY) {
|
|
237
|
+
flags.TRY = alphabet.concat();
|
|
238
|
+
}
|
|
239
|
+
if (!flags.KEEPCASE) {
|
|
240
|
+
flags.KEEPCASE = false;
|
|
241
|
+
}
|
|
242
|
+
return {
|
|
243
|
+
compoundRuleCodes,
|
|
244
|
+
replacementTable,
|
|
245
|
+
conversion,
|
|
246
|
+
compoundRules,
|
|
247
|
+
rules,
|
|
248
|
+
flags
|
|
249
|
+
};
|
|
250
|
+
function pushLine(line2) {
|
|
251
|
+
line2 = line2.trim();
|
|
252
|
+
if (line2 && line2.charCodeAt(0) !== 35) {
|
|
253
|
+
lines.push(line2);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
function end(source) {
|
|
258
|
+
return new RegExp(source + "$");
|
|
259
|
+
}
|
|
260
|
+
function start(source) {
|
|
261
|
+
return new RegExp("^" + source);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
// node_modules/nspell/lib/util/normalize.js
|
|
267
|
+
var require_normalize = __commonJS({
|
|
268
|
+
"node_modules/nspell/lib/util/normalize.js"(exports, module) {
|
|
269
|
+
"use strict";
|
|
270
|
+
module.exports = normalize;
|
|
271
|
+
function normalize(value, patterns) {
|
|
272
|
+
var index = -1;
|
|
273
|
+
while (++index < patterns.length) {
|
|
274
|
+
value = value.replace(patterns[index][0], patterns[index][1]);
|
|
275
|
+
}
|
|
276
|
+
return value;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
// node_modules/nspell/lib/util/flag.js
|
|
282
|
+
var require_flag = __commonJS({
|
|
283
|
+
"node_modules/nspell/lib/util/flag.js"(exports, module) {
|
|
284
|
+
"use strict";
|
|
285
|
+
module.exports = flag;
|
|
286
|
+
function flag(values, value, flags) {
|
|
287
|
+
return flags && value in values && flags.indexOf(values[value]) > -1;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
// node_modules/nspell/lib/util/exact.js
|
|
293
|
+
var require_exact = __commonJS({
|
|
294
|
+
"node_modules/nspell/lib/util/exact.js"(exports, module) {
|
|
295
|
+
"use strict";
|
|
296
|
+
var flag = require_flag();
|
|
297
|
+
module.exports = exact;
|
|
298
|
+
function exact(context, value) {
|
|
299
|
+
var index = -1;
|
|
300
|
+
if (context.data[value]) {
|
|
301
|
+
return !flag(context.flags, "ONLYINCOMPOUND", context.data[value]);
|
|
302
|
+
}
|
|
303
|
+
if (value.length >= context.flags.COMPOUNDMIN) {
|
|
304
|
+
while (++index < context.compoundRules.length) {
|
|
305
|
+
if (context.compoundRules[index].test(value)) {
|
|
306
|
+
return true;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
// node_modules/nspell/lib/util/form.js
|
|
316
|
+
var require_form = __commonJS({
|
|
317
|
+
"node_modules/nspell/lib/util/form.js"(exports, module) {
|
|
318
|
+
"use strict";
|
|
319
|
+
var normalize = require_normalize();
|
|
320
|
+
var exact = require_exact();
|
|
321
|
+
var flag = require_flag();
|
|
322
|
+
module.exports = form;
|
|
323
|
+
function form(context, value, all) {
|
|
324
|
+
var normal = value.trim();
|
|
325
|
+
var alternative;
|
|
326
|
+
if (!normal) {
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
normal = normalize(normal, context.conversion.in);
|
|
330
|
+
if (exact(context, normal)) {
|
|
331
|
+
if (!all && flag(context.flags, "FORBIDDENWORD", context.data[normal])) {
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
return normal;
|
|
335
|
+
}
|
|
336
|
+
if (normal.toUpperCase() === normal) {
|
|
337
|
+
alternative = normal.charAt(0) + normal.slice(1).toLowerCase();
|
|
338
|
+
if (ignore(context.flags, context.data[alternative], all)) {
|
|
339
|
+
return null;
|
|
340
|
+
}
|
|
341
|
+
if (exact(context, alternative)) {
|
|
342
|
+
return alternative;
|
|
914
343
|
}
|
|
915
|
-
};
|
|
916
|
-
try {
|
|
917
|
-
place();
|
|
918
|
-
editor2.getWin()?.requestAnimationFrame?.(() => {
|
|
919
|
-
try {
|
|
920
|
-
place();
|
|
921
|
-
} catch {
|
|
922
|
-
}
|
|
923
|
-
});
|
|
924
|
-
} catch {
|
|
925
344
|
}
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
345
|
+
alternative = normal.toLowerCase();
|
|
346
|
+
if (alternative !== normal) {
|
|
347
|
+
if (ignore(context.flags, context.data[alternative], all)) {
|
|
348
|
+
return null;
|
|
349
|
+
}
|
|
350
|
+
if (exact(context, alternative)) {
|
|
351
|
+
return alternative;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
return null;
|
|
355
|
+
}
|
|
356
|
+
function ignore(flags, dict, all) {
|
|
357
|
+
return flag(flags, "KEEPCASE", dict) || all || flag(flags, "FORBIDDENWORD", dict);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
// node_modules/nspell/lib/correct.js
|
|
363
|
+
var require_correct = __commonJS({
|
|
364
|
+
"node_modules/nspell/lib/correct.js"(exports, module) {
|
|
941
365
|
"use strict";
|
|
366
|
+
var form = require_form();
|
|
367
|
+
module.exports = correct;
|
|
368
|
+
function correct(value) {
|
|
369
|
+
return Boolean(form(this, value));
|
|
370
|
+
}
|
|
942
371
|
}
|
|
943
372
|
});
|
|
944
373
|
|
|
945
|
-
// node_modules/
|
|
946
|
-
var
|
|
947
|
-
"node_modules/
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
374
|
+
// node_modules/nspell/lib/util/casing.js
|
|
375
|
+
var require_casing = __commonJS({
|
|
376
|
+
"node_modules/nspell/lib/util/casing.js"(exports, module) {
|
|
377
|
+
"use strict";
|
|
378
|
+
module.exports = casing;
|
|
379
|
+
function casing(value) {
|
|
380
|
+
var head = exact(value.charAt(0));
|
|
381
|
+
var rest = value.slice(1);
|
|
382
|
+
if (!rest) {
|
|
383
|
+
return head;
|
|
384
|
+
}
|
|
385
|
+
rest = exact(rest);
|
|
386
|
+
if (head === rest) {
|
|
387
|
+
return head;
|
|
388
|
+
}
|
|
389
|
+
if (head === "u" && rest === "l") {
|
|
390
|
+
return "s";
|
|
391
|
+
}
|
|
392
|
+
return null;
|
|
393
|
+
}
|
|
394
|
+
function exact(value) {
|
|
395
|
+
return value === value.toLowerCase() ? "l" : value === value.toUpperCase() ? "u" : null;
|
|
396
|
+
}
|
|
951
397
|
}
|
|
952
398
|
});
|
|
953
399
|
|
|
954
|
-
// node_modules/nspell/lib/
|
|
955
|
-
var
|
|
956
|
-
"node_modules/nspell/lib/
|
|
400
|
+
// node_modules/nspell/lib/suggest.js
|
|
401
|
+
var require_suggest = __commonJS({
|
|
402
|
+
"node_modules/nspell/lib/suggest.js"(exports, module) {
|
|
957
403
|
"use strict";
|
|
958
|
-
|
|
959
|
-
var
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
404
|
+
var casing = require_casing();
|
|
405
|
+
var normalize = require_normalize();
|
|
406
|
+
var flag = require_flag();
|
|
407
|
+
var form = require_form();
|
|
408
|
+
module.exports = suggest;
|
|
409
|
+
var push = [].push;
|
|
410
|
+
function suggest(value) {
|
|
411
|
+
var self = this;
|
|
412
|
+
var charAdded = {};
|
|
413
|
+
var suggestions = [];
|
|
414
|
+
var weighted = {};
|
|
415
|
+
var memory;
|
|
416
|
+
var replacement;
|
|
417
|
+
var edits = [];
|
|
418
|
+
var values;
|
|
419
|
+
var index;
|
|
420
|
+
var offset;
|
|
421
|
+
var position;
|
|
422
|
+
var count;
|
|
423
|
+
var otherOffset;
|
|
424
|
+
var otherCharacter;
|
|
425
|
+
var character;
|
|
426
|
+
var group;
|
|
427
|
+
var before;
|
|
428
|
+
var after;
|
|
429
|
+
var upper;
|
|
430
|
+
var insensitive;
|
|
431
|
+
var firstLevel;
|
|
432
|
+
var previous;
|
|
433
|
+
var next;
|
|
434
|
+
var nextCharacter;
|
|
435
|
+
var max;
|
|
436
|
+
var distance;
|
|
437
|
+
var size;
|
|
438
|
+
var normalized;
|
|
439
|
+
var suggestion;
|
|
440
|
+
var currentCase;
|
|
441
|
+
value = normalize(value.trim(), self.conversion.in);
|
|
442
|
+
if (!value || self.correct(value)) {
|
|
443
|
+
return [];
|
|
444
|
+
}
|
|
445
|
+
currentCase = casing(value);
|
|
446
|
+
index = -1;
|
|
447
|
+
while (++index < self.replacementTable.length) {
|
|
448
|
+
replacement = self.replacementTable[index];
|
|
449
|
+
offset = value.indexOf(replacement[0]);
|
|
450
|
+
while (offset > -1) {
|
|
451
|
+
edits.push(value.replace(replacement[0], replacement[1]));
|
|
452
|
+
offset = value.indexOf(replacement[0], offset + 1);
|
|
969
453
|
}
|
|
970
|
-
return result;
|
|
971
454
|
}
|
|
972
|
-
|
|
455
|
+
index = -1;
|
|
456
|
+
while (++index < value.length) {
|
|
457
|
+
character = value.charAt(index);
|
|
458
|
+
before = value.slice(0, index);
|
|
459
|
+
after = value.slice(index + 1);
|
|
460
|
+
insensitive = character.toLowerCase();
|
|
461
|
+
upper = insensitive !== character;
|
|
462
|
+
charAdded = {};
|
|
463
|
+
offset = -1;
|
|
464
|
+
while (++offset < self.flags.KEY.length) {
|
|
465
|
+
group = self.flags.KEY[offset];
|
|
466
|
+
position = group.indexOf(insensitive);
|
|
467
|
+
if (position < 0) {
|
|
468
|
+
continue;
|
|
469
|
+
}
|
|
470
|
+
otherOffset = -1;
|
|
471
|
+
while (++otherOffset < group.length) {
|
|
472
|
+
if (otherOffset !== position) {
|
|
473
|
+
otherCharacter = group.charAt(otherOffset);
|
|
474
|
+
if (charAdded[otherCharacter]) {
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
477
|
+
charAdded[otherCharacter] = true;
|
|
478
|
+
if (upper) {
|
|
479
|
+
otherCharacter = otherCharacter.toUpperCase();
|
|
480
|
+
}
|
|
481
|
+
edits.push(before + otherCharacter + after);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
index = -1;
|
|
487
|
+
nextCharacter = value.charAt(0);
|
|
488
|
+
values = [""];
|
|
489
|
+
max = 1;
|
|
490
|
+
distance = 0;
|
|
491
|
+
while (++index < value.length) {
|
|
492
|
+
character = nextCharacter;
|
|
493
|
+
nextCharacter = value.charAt(index + 1);
|
|
494
|
+
before = value.slice(0, index);
|
|
495
|
+
replacement = character === nextCharacter ? "" : character + character;
|
|
496
|
+
offset = -1;
|
|
497
|
+
count = values.length;
|
|
498
|
+
while (++offset < count) {
|
|
499
|
+
if (offset <= max) {
|
|
500
|
+
values.push(values[offset] + replacement);
|
|
501
|
+
}
|
|
502
|
+
values[offset] += character;
|
|
503
|
+
}
|
|
504
|
+
if (++distance < 3) {
|
|
505
|
+
max = values.length;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
push.apply(edits, values);
|
|
509
|
+
values = [value];
|
|
510
|
+
replacement = value.toLowerCase();
|
|
511
|
+
if (value === replacement || currentCase === null) {
|
|
512
|
+
values.push(value.charAt(0).toUpperCase() + replacement.slice(1));
|
|
513
|
+
}
|
|
514
|
+
replacement = value.toUpperCase();
|
|
515
|
+
if (value !== replacement) {
|
|
516
|
+
values.push(replacement);
|
|
517
|
+
}
|
|
518
|
+
memory = {
|
|
519
|
+
state: {},
|
|
520
|
+
weighted,
|
|
521
|
+
suggestions
|
|
522
|
+
};
|
|
523
|
+
firstLevel = generate(self, memory, values, edits);
|
|
524
|
+
previous = 0;
|
|
525
|
+
max = Math.min(firstLevel.length, Math.pow(Math.max(15 - value.length, 3), 3));
|
|
526
|
+
size = Math.max(Math.pow(10 - value.length, 3), 1);
|
|
527
|
+
while (!suggestions.length && previous < max) {
|
|
528
|
+
next = previous + size;
|
|
529
|
+
generate(self, memory, firstLevel.slice(previous, next));
|
|
530
|
+
previous = next;
|
|
531
|
+
}
|
|
532
|
+
suggestions.sort(sort);
|
|
533
|
+
values = [];
|
|
534
|
+
normalized = [];
|
|
535
|
+
index = -1;
|
|
536
|
+
while (++index < suggestions.length) {
|
|
537
|
+
suggestion = normalize(suggestions[index], self.conversion.out);
|
|
538
|
+
replacement = suggestion.toLowerCase();
|
|
539
|
+
if (normalized.indexOf(replacement) < 0) {
|
|
540
|
+
values.push(suggestion);
|
|
541
|
+
normalized.push(replacement);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
return values;
|
|
545
|
+
function sort(a, b) {
|
|
546
|
+
return sortWeight(a, b) || sortCasing(a, b) || sortAlpha(a, b);
|
|
547
|
+
}
|
|
548
|
+
function sortWeight(a, b) {
|
|
549
|
+
return weighted[a] === weighted[b] ? 0 : weighted[a] > weighted[b] ? -1 : 1;
|
|
550
|
+
}
|
|
551
|
+
function sortCasing(a, b) {
|
|
552
|
+
var leftCasing = casing(a);
|
|
553
|
+
var rightCasing = casing(b);
|
|
554
|
+
return leftCasing === rightCasing ? 0 : leftCasing === currentCase ? -1 : rightCasing === currentCase ? 1 : void 0;
|
|
555
|
+
}
|
|
556
|
+
function sortAlpha(a, b) {
|
|
557
|
+
return a.localeCompare(b);
|
|
558
|
+
}
|
|
973
559
|
}
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
var
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
"yxcvbnm",
|
|
989
|
-
"qaw",
|
|
990
|
-
"say",
|
|
991
|
-
"wse",
|
|
992
|
-
"dsx",
|
|
993
|
-
"sy",
|
|
994
|
-
"edr",
|
|
995
|
-
"fdc",
|
|
996
|
-
"dx",
|
|
997
|
-
"rft",
|
|
998
|
-
"gfv",
|
|
999
|
-
"fc",
|
|
1000
|
-
"tgz",
|
|
1001
|
-
"hgb",
|
|
1002
|
-
"gv",
|
|
1003
|
-
"zhu",
|
|
1004
|
-
"jhn",
|
|
1005
|
-
"hb",
|
|
1006
|
-
"uji",
|
|
1007
|
-
"kjm",
|
|
1008
|
-
"jn",
|
|
1009
|
-
"iko",
|
|
1010
|
-
"lkm"
|
|
1011
|
-
];
|
|
1012
|
-
function affix(doc) {
|
|
1013
|
-
var rules = /* @__PURE__ */ Object.create(null);
|
|
1014
|
-
var compoundRuleCodes = /* @__PURE__ */ Object.create(null);
|
|
1015
|
-
var flags = /* @__PURE__ */ Object.create(null);
|
|
1016
|
-
var replacementTable = [];
|
|
1017
|
-
var conversion = { in: [], out: [] };
|
|
1018
|
-
var compoundRules = [];
|
|
1019
|
-
var aff = doc.toString("utf8");
|
|
1020
|
-
var lines = [];
|
|
1021
|
-
var last = 0;
|
|
1022
|
-
var index = aff.indexOf("\n");
|
|
1023
|
-
var parts;
|
|
1024
|
-
var line;
|
|
1025
|
-
var ruleType;
|
|
1026
|
-
var count;
|
|
1027
|
-
var remove;
|
|
1028
|
-
var add;
|
|
1029
|
-
var source;
|
|
1030
|
-
var entry;
|
|
560
|
+
function generate(context, memory, words, edits) {
|
|
561
|
+
var characters = context.flags.TRY;
|
|
562
|
+
var data = context.data;
|
|
563
|
+
var flags = context.flags;
|
|
564
|
+
var result = [];
|
|
565
|
+
var index = -1;
|
|
566
|
+
var word;
|
|
567
|
+
var before;
|
|
568
|
+
var character;
|
|
569
|
+
var nextCharacter;
|
|
570
|
+
var nextAfter;
|
|
571
|
+
var nextNextAfter;
|
|
572
|
+
var nextUpper;
|
|
573
|
+
var currentCase;
|
|
1031
574
|
var position;
|
|
1032
|
-
var
|
|
1033
|
-
var
|
|
575
|
+
var after;
|
|
576
|
+
var upper;
|
|
577
|
+
var inject;
|
|
1034
578
|
var offset;
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
last = index + 1;
|
|
1040
|
-
index = aff.indexOf("\n", last);
|
|
579
|
+
if (edits) {
|
|
580
|
+
while (++index < edits.length) {
|
|
581
|
+
check(edits[index], true);
|
|
582
|
+
}
|
|
1041
583
|
}
|
|
1042
|
-
pushLine(aff.slice(last));
|
|
1043
584
|
index = -1;
|
|
1044
|
-
while (++index <
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
585
|
+
while (++index < words.length) {
|
|
586
|
+
word = words[index];
|
|
587
|
+
before = "";
|
|
588
|
+
character = "";
|
|
589
|
+
nextCharacter = word.charAt(0);
|
|
590
|
+
nextAfter = word;
|
|
591
|
+
nextNextAfter = word.slice(1);
|
|
592
|
+
nextUpper = nextCharacter.toLowerCase() !== nextCharacter;
|
|
593
|
+
currentCase = casing(word);
|
|
594
|
+
position = -1;
|
|
595
|
+
while (++position <= word.length) {
|
|
596
|
+
before += character;
|
|
597
|
+
after = nextAfter;
|
|
598
|
+
nextAfter = nextNextAfter;
|
|
599
|
+
nextNextAfter = nextAfter.slice(1);
|
|
600
|
+
character = nextCharacter;
|
|
601
|
+
nextCharacter = word.charAt(position + 1);
|
|
602
|
+
upper = nextUpper;
|
|
603
|
+
if (nextCharacter) {
|
|
604
|
+
nextUpper = nextCharacter.toLowerCase() !== nextCharacter;
|
|
1053
605
|
}
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
parts = lines[index].split(whiteSpaceExpression);
|
|
1060
|
-
entry.push([new RegExp(parts[1], "g"), parts[2]]);
|
|
606
|
+
if (nextAfter && upper !== nextUpper) {
|
|
607
|
+
check(before + switchCase(nextAfter));
|
|
608
|
+
check(
|
|
609
|
+
before + switchCase(nextCharacter) + switchCase(character) + nextNextAfter
|
|
610
|
+
);
|
|
1061
611
|
}
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
while (++index <= count) {
|
|
1066
|
-
rule = lines[index].split(whiteSpaceExpression)[1];
|
|
1067
|
-
position = -1;
|
|
1068
|
-
compoundRules.push(rule);
|
|
1069
|
-
while (++position < rule.length) {
|
|
1070
|
-
compoundRuleCodes[rule.charAt(position)] = [];
|
|
1071
|
-
}
|
|
612
|
+
check(before + nextAfter);
|
|
613
|
+
if (nextAfter) {
|
|
614
|
+
check(before + nextCharacter + character + nextNextAfter);
|
|
1072
615
|
}
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
};
|
|
1081
|
-
rules[parts[1]] = rule;
|
|
1082
|
-
while (++index <= count) {
|
|
1083
|
-
parts = lines[index].split(whiteSpaceExpression);
|
|
1084
|
-
remove = parts[2];
|
|
1085
|
-
add = parts[3].split("/");
|
|
1086
|
-
source = parts[4];
|
|
1087
|
-
entry = {
|
|
1088
|
-
add: "",
|
|
1089
|
-
remove: "",
|
|
1090
|
-
match: "",
|
|
1091
|
-
continuation: parse(flags, add[1])
|
|
1092
|
-
};
|
|
1093
|
-
if (add && add[0] !== "0") {
|
|
1094
|
-
entry.add = add[0];
|
|
1095
|
-
}
|
|
1096
|
-
try {
|
|
1097
|
-
if (remove !== "0") {
|
|
1098
|
-
entry.remove = ruleType === "SFX" ? end(remove) : remove;
|
|
1099
|
-
}
|
|
1100
|
-
if (source && source !== ".") {
|
|
1101
|
-
entry.match = ruleType === "SFX" ? end(source) : start(source);
|
|
616
|
+
offset = -1;
|
|
617
|
+
while (++offset < characters.length) {
|
|
618
|
+
inject = characters[offset];
|
|
619
|
+
if (upper && inject !== inject.toUpperCase()) {
|
|
620
|
+
if (currentCase !== "s") {
|
|
621
|
+
check(before + inject + after);
|
|
622
|
+
check(before + inject + nextAfter);
|
|
1102
623
|
}
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
624
|
+
inject = inject.toUpperCase();
|
|
625
|
+
check(before + inject + after);
|
|
626
|
+
check(before + inject + nextAfter);
|
|
627
|
+
} else {
|
|
628
|
+
check(before + inject + after);
|
|
629
|
+
check(before + inject + nextAfter);
|
|
1108
630
|
}
|
|
1109
631
|
}
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
return result;
|
|
635
|
+
function check(value, double) {
|
|
636
|
+
var state = memory.state[value];
|
|
637
|
+
var corrected;
|
|
638
|
+
if (state !== Boolean(state)) {
|
|
639
|
+
result.push(value);
|
|
640
|
+
corrected = form(context, value);
|
|
641
|
+
state = corrected && !flag(flags, "NOSUGGEST", data[corrected]);
|
|
642
|
+
memory.state[value] = state;
|
|
643
|
+
if (state) {
|
|
644
|
+
memory.weighted[value] = double ? 10 : 0;
|
|
645
|
+
memory.suggestions.push(value);
|
|
1120
646
|
}
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
647
|
+
}
|
|
648
|
+
if (state) {
|
|
649
|
+
memory.weighted[value]++;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
function switchCase(fragment) {
|
|
653
|
+
var first = fragment.charAt(0);
|
|
654
|
+
return (first.toLowerCase() === first ? first.toUpperCase() : first.toLowerCase()) + fragment.slice(1);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
// node_modules/nspell/lib/spell.js
|
|
661
|
+
var require_spell = __commonJS({
|
|
662
|
+
"node_modules/nspell/lib/spell.js"(exports, module) {
|
|
663
|
+
"use strict";
|
|
664
|
+
var form = require_form();
|
|
665
|
+
var flag = require_flag();
|
|
666
|
+
module.exports = spell;
|
|
667
|
+
function spell(word) {
|
|
668
|
+
var self = this;
|
|
669
|
+
var value = form(self, word, true);
|
|
670
|
+
return {
|
|
671
|
+
correct: self.correct(word),
|
|
672
|
+
forbidden: Boolean(
|
|
673
|
+
value && flag(self.flags, "FORBIDDENWORD", self.data[value])
|
|
674
|
+
),
|
|
675
|
+
warn: Boolean(value && flag(self.flags, "WARN", self.data[value]))
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
});
|
|
680
|
+
|
|
681
|
+
// node_modules/nspell/lib/util/apply.js
|
|
682
|
+
var require_apply = __commonJS({
|
|
683
|
+
"node_modules/nspell/lib/util/apply.js"(exports, module) {
|
|
684
|
+
"use strict";
|
|
685
|
+
module.exports = apply;
|
|
686
|
+
function apply(value, rule, rules, words) {
|
|
687
|
+
var index = -1;
|
|
688
|
+
var entry;
|
|
689
|
+
var next;
|
|
690
|
+
var continuationRule;
|
|
691
|
+
var continuation;
|
|
692
|
+
var position;
|
|
693
|
+
while (++index < rule.entries.length) {
|
|
694
|
+
entry = rule.entries[index];
|
|
695
|
+
continuation = entry.continuation;
|
|
696
|
+
position = -1;
|
|
697
|
+
if (!entry.match || entry.match.test(value)) {
|
|
698
|
+
next = entry.remove ? value.replace(entry.remove, "") : value;
|
|
699
|
+
next = rule.type === "SFX" ? next + entry.add : entry.add + next;
|
|
700
|
+
words.push(next);
|
|
701
|
+
if (continuation && continuation.length) {
|
|
702
|
+
while (++position < continuation.length) {
|
|
703
|
+
continuationRule = rules[continuation[position]];
|
|
704
|
+
if (continuationRule) {
|
|
705
|
+
apply(next, continuationRule, rules, words);
|
|
706
|
+
}
|
|
1125
707
|
}
|
|
1126
708
|
}
|
|
1127
|
-
flags[ruleType] = value;
|
|
1128
|
-
} else if (ruleType === "KEY") {
|
|
1129
|
-
push.apply(flags[ruleType], parts[1].split("|"));
|
|
1130
|
-
} else if (ruleType === "COMPOUNDMIN") {
|
|
1131
|
-
flags[ruleType] = Number(parts[1]);
|
|
1132
|
-
} else if (ruleType === "ONLYINCOMPOUND") {
|
|
1133
|
-
flags[ruleType] = parts[1];
|
|
1134
|
-
compoundRuleCodes[parts[1]] = [];
|
|
1135
|
-
} else if (ruleType === "FLAG" || ruleType === "KEEPCASE" || ruleType === "NOSUGGEST" || ruleType === "WORDCHARS") {
|
|
1136
|
-
flags[ruleType] = parts[1];
|
|
1137
|
-
} else {
|
|
1138
|
-
flags[ruleType] = parts[1];
|
|
1139
709
|
}
|
|
1140
710
|
}
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
711
|
+
return words;
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
});
|
|
715
|
+
|
|
716
|
+
// node_modules/nspell/lib/util/add.js
|
|
717
|
+
var require_add = __commonJS({
|
|
718
|
+
"node_modules/nspell/lib/util/add.js"(exports, module) {
|
|
719
|
+
"use strict";
|
|
720
|
+
var apply = require_apply();
|
|
721
|
+
module.exports = add;
|
|
722
|
+
var push = [].push;
|
|
723
|
+
var NO_RULES = [];
|
|
724
|
+
function addRules(dict, word, rules) {
|
|
725
|
+
var curr = dict[word];
|
|
726
|
+
if (word in dict) {
|
|
727
|
+
if (curr === NO_RULES) {
|
|
728
|
+
dict[word] = rules.concat();
|
|
729
|
+
} else {
|
|
730
|
+
push.apply(curr, rules);
|
|
731
|
+
}
|
|
732
|
+
} else {
|
|
733
|
+
dict[word] = rules.concat();
|
|
1149
734
|
}
|
|
1150
|
-
|
|
1151
|
-
|
|
735
|
+
}
|
|
736
|
+
function add(dict, word, codes, options) {
|
|
737
|
+
var position = -1;
|
|
738
|
+
var rule;
|
|
739
|
+
var offset;
|
|
740
|
+
var subposition;
|
|
741
|
+
var suboffset;
|
|
742
|
+
var combined;
|
|
743
|
+
var newWords;
|
|
744
|
+
var otherNewWords;
|
|
745
|
+
if (!("NEEDAFFIX" in options.flags) || codes.indexOf(options.flags.NEEDAFFIX) < 0) {
|
|
746
|
+
addRules(dict, word, codes);
|
|
1152
747
|
}
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
748
|
+
while (++position < codes.length) {
|
|
749
|
+
rule = options.rules[codes[position]];
|
|
750
|
+
if (codes[position] in options.compoundRuleCodes) {
|
|
751
|
+
options.compoundRuleCodes[codes[position]].push(word);
|
|
752
|
+
}
|
|
753
|
+
if (rule) {
|
|
754
|
+
newWords = apply(word, rule, options.rules, []);
|
|
755
|
+
offset = -1;
|
|
756
|
+
while (++offset < newWords.length) {
|
|
757
|
+
if (!(newWords[offset] in dict)) {
|
|
758
|
+
dict[newWords[offset]] = NO_RULES;
|
|
759
|
+
}
|
|
760
|
+
if (rule.combineable) {
|
|
761
|
+
subposition = position;
|
|
762
|
+
while (++subposition < codes.length) {
|
|
763
|
+
combined = options.rules[codes[subposition]];
|
|
764
|
+
if (combined && combined.combineable && rule.type !== combined.type) {
|
|
765
|
+
otherNewWords = apply(
|
|
766
|
+
newWords[offset],
|
|
767
|
+
combined,
|
|
768
|
+
options.rules,
|
|
769
|
+
[]
|
|
770
|
+
);
|
|
771
|
+
suboffset = -1;
|
|
772
|
+
while (++suboffset < otherNewWords.length) {
|
|
773
|
+
if (!(otherNewWords[suboffset] in dict)) {
|
|
774
|
+
dict[otherNewWords[suboffset]] = NO_RULES;
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
}
|
|
1165
781
|
}
|
|
1166
782
|
}
|
|
1167
783
|
}
|
|
1168
|
-
function end(source) {
|
|
1169
|
-
return new RegExp(source + "$");
|
|
1170
|
-
}
|
|
1171
|
-
function start(source) {
|
|
1172
|
-
return new RegExp("^" + source);
|
|
1173
|
-
}
|
|
1174
784
|
}
|
|
1175
785
|
});
|
|
1176
786
|
|
|
1177
|
-
// node_modules/nspell/lib/
|
|
1178
|
-
var
|
|
1179
|
-
"node_modules/nspell/lib/
|
|
787
|
+
// node_modules/nspell/lib/add.js
|
|
788
|
+
var require_add2 = __commonJS({
|
|
789
|
+
"node_modules/nspell/lib/add.js"(exports, module) {
|
|
1180
790
|
"use strict";
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
return
|
|
791
|
+
var push = require_add();
|
|
792
|
+
module.exports = add;
|
|
793
|
+
var NO_CODES = [];
|
|
794
|
+
function add(value, model) {
|
|
795
|
+
var self = this;
|
|
796
|
+
push(self.data, value, self.data[model] || NO_CODES, self);
|
|
797
|
+
return self;
|
|
1188
798
|
}
|
|
1189
799
|
}
|
|
1190
800
|
});
|
|
1191
801
|
|
|
1192
|
-
// node_modules/nspell/lib/
|
|
1193
|
-
var
|
|
1194
|
-
"node_modules/nspell/lib/
|
|
802
|
+
// node_modules/nspell/lib/remove.js
|
|
803
|
+
var require_remove = __commonJS({
|
|
804
|
+
"node_modules/nspell/lib/remove.js"(exports, module) {
|
|
1195
805
|
"use strict";
|
|
1196
|
-
module.exports =
|
|
1197
|
-
function
|
|
1198
|
-
|
|
806
|
+
module.exports = remove;
|
|
807
|
+
function remove(value) {
|
|
808
|
+
var self = this;
|
|
809
|
+
delete self.data[value];
|
|
810
|
+
return self;
|
|
1199
811
|
}
|
|
1200
812
|
}
|
|
1201
813
|
});
|
|
1202
814
|
|
|
1203
|
-
// node_modules/nspell/lib/
|
|
1204
|
-
var
|
|
1205
|
-
"node_modules/nspell/lib/
|
|
815
|
+
// node_modules/nspell/lib/word-characters.js
|
|
816
|
+
var require_word_characters = __commonJS({
|
|
817
|
+
"node_modules/nspell/lib/word-characters.js"(exports, module) {
|
|
1206
818
|
"use strict";
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
var index = -1;
|
|
1211
|
-
if (context.data[value]) {
|
|
1212
|
-
return !flag(context.flags, "ONLYINCOMPOUND", context.data[value]);
|
|
1213
|
-
}
|
|
1214
|
-
if (value.length >= context.flags.COMPOUNDMIN) {
|
|
1215
|
-
while (++index < context.compoundRules.length) {
|
|
1216
|
-
if (context.compoundRules[index].test(value)) {
|
|
1217
|
-
return true;
|
|
1218
|
-
}
|
|
1219
|
-
}
|
|
1220
|
-
}
|
|
1221
|
-
return false;
|
|
819
|
+
module.exports = wordCharacters;
|
|
820
|
+
function wordCharacters() {
|
|
821
|
+
return this.flags.WORDCHARS || null;
|
|
1222
822
|
}
|
|
1223
823
|
}
|
|
1224
824
|
});
|
|
1225
825
|
|
|
1226
|
-
// node_modules/nspell/lib/util/
|
|
1227
|
-
var
|
|
1228
|
-
"node_modules/nspell/lib/util/
|
|
826
|
+
// node_modules/nspell/lib/util/dictionary.js
|
|
827
|
+
var require_dictionary = __commonJS({
|
|
828
|
+
"node_modules/nspell/lib/util/dictionary.js"(exports, module) {
|
|
1229
829
|
"use strict";
|
|
1230
|
-
var
|
|
1231
|
-
var
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
function
|
|
1235
|
-
var
|
|
1236
|
-
var
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
if (exact(context, normal)) {
|
|
1242
|
-
if (!all && flag(context.flags, "FORBIDDENWORD", context.data[normal])) {
|
|
1243
|
-
return null;
|
|
830
|
+
var parseCodes = require_rule_codes();
|
|
831
|
+
var add = require_add();
|
|
832
|
+
module.exports = parse;
|
|
833
|
+
var whiteSpaceExpression = /\s/g;
|
|
834
|
+
function parse(buf, options, dict) {
|
|
835
|
+
var value = buf.toString("utf8");
|
|
836
|
+
var last = value.indexOf("\n") + 1;
|
|
837
|
+
var index = value.indexOf("\n", last);
|
|
838
|
+
while (index > -1) {
|
|
839
|
+
if (value.charCodeAt(last) !== 9) {
|
|
840
|
+
parseLine(value.slice(last, index), options, dict);
|
|
1244
841
|
}
|
|
1245
|
-
|
|
842
|
+
last = index + 1;
|
|
843
|
+
index = value.indexOf("\n", last);
|
|
1246
844
|
}
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
845
|
+
parseLine(value.slice(last), options, dict);
|
|
846
|
+
}
|
|
847
|
+
function parseLine(line, options, dict) {
|
|
848
|
+
var slashOffset = line.indexOf("/");
|
|
849
|
+
var hashOffset = line.indexOf("#");
|
|
850
|
+
var codes = "";
|
|
851
|
+
var word;
|
|
852
|
+
var result;
|
|
853
|
+
while (slashOffset > -1 && line.charCodeAt(slashOffset - 1) === 92) {
|
|
854
|
+
line = line.slice(0, slashOffset - 1) + line.slice(slashOffset);
|
|
855
|
+
slashOffset = line.indexOf("/", slashOffset);
|
|
1255
856
|
}
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
857
|
+
if (hashOffset > -1) {
|
|
858
|
+
if (slashOffset > -1 && slashOffset < hashOffset) {
|
|
859
|
+
word = line.slice(0, slashOffset);
|
|
860
|
+
whiteSpaceExpression.lastIndex = slashOffset + 1;
|
|
861
|
+
result = whiteSpaceExpression.exec(line);
|
|
862
|
+
codes = line.slice(slashOffset + 1, result ? result.index : void 0);
|
|
863
|
+
} else {
|
|
864
|
+
word = line.slice(0, hashOffset);
|
|
1263
865
|
}
|
|
866
|
+
} else if (slashOffset > -1) {
|
|
867
|
+
word = line.slice(0, slashOffset);
|
|
868
|
+
codes = line.slice(slashOffset + 1);
|
|
869
|
+
} else {
|
|
870
|
+
word = line;
|
|
871
|
+
}
|
|
872
|
+
word = word.trim();
|
|
873
|
+
if (word) {
|
|
874
|
+
add(dict, word, parseCodes(options.flags, codes.trim()), options);
|
|
1264
875
|
}
|
|
1265
|
-
return null;
|
|
1266
|
-
}
|
|
1267
|
-
function ignore(flags, dict, all) {
|
|
1268
|
-
return flag(flags, "KEEPCASE", dict) || all || flag(flags, "FORBIDDENWORD", dict);
|
|
1269
876
|
}
|
|
1270
877
|
}
|
|
1271
878
|
});
|
|
1272
879
|
|
|
1273
|
-
// node_modules/nspell/lib/
|
|
1274
|
-
var
|
|
1275
|
-
"node_modules/nspell/lib/
|
|
880
|
+
// node_modules/nspell/lib/dictionary.js
|
|
881
|
+
var require_dictionary2 = __commonJS({
|
|
882
|
+
"node_modules/nspell/lib/dictionary.js"(exports, module) {
|
|
1276
883
|
"use strict";
|
|
1277
|
-
var
|
|
1278
|
-
module.exports =
|
|
1279
|
-
function
|
|
1280
|
-
|
|
884
|
+
var parse = require_dictionary();
|
|
885
|
+
module.exports = add;
|
|
886
|
+
function add(buf) {
|
|
887
|
+
var self = this;
|
|
888
|
+
var index = -1;
|
|
889
|
+
var rule;
|
|
890
|
+
var source;
|
|
891
|
+
var character;
|
|
892
|
+
var offset;
|
|
893
|
+
parse(buf, self, self.data);
|
|
894
|
+
while (++index < self.compoundRules.length) {
|
|
895
|
+
rule = self.compoundRules[index];
|
|
896
|
+
source = "";
|
|
897
|
+
offset = -1;
|
|
898
|
+
while (++offset < rule.length) {
|
|
899
|
+
character = rule.charAt(offset);
|
|
900
|
+
source += self.compoundRuleCodes[character].length ? "(?:" + self.compoundRuleCodes[character].join("|") + ")" : character;
|
|
901
|
+
}
|
|
902
|
+
self.compoundRules[index] = new RegExp(source, "i");
|
|
903
|
+
}
|
|
904
|
+
return self;
|
|
1281
905
|
}
|
|
1282
906
|
}
|
|
1283
907
|
});
|
|
1284
908
|
|
|
1285
|
-
// node_modules/nspell/lib/
|
|
1286
|
-
var
|
|
1287
|
-
"node_modules/nspell/lib/
|
|
909
|
+
// node_modules/nspell/lib/personal.js
|
|
910
|
+
var require_personal = __commonJS({
|
|
911
|
+
"node_modules/nspell/lib/personal.js"(exports, module) {
|
|
1288
912
|
"use strict";
|
|
1289
|
-
module.exports =
|
|
1290
|
-
function
|
|
1291
|
-
var
|
|
1292
|
-
var
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
913
|
+
module.exports = add;
|
|
914
|
+
function add(buf) {
|
|
915
|
+
var self = this;
|
|
916
|
+
var lines = buf.toString("utf8").split("\n");
|
|
917
|
+
var index = -1;
|
|
918
|
+
var line;
|
|
919
|
+
var forbidden;
|
|
920
|
+
var word;
|
|
921
|
+
var flag;
|
|
922
|
+
if (self.flags.FORBIDDENWORD === void 0) self.flags.FORBIDDENWORD = false;
|
|
923
|
+
flag = self.flags.FORBIDDENWORD;
|
|
924
|
+
while (++index < lines.length) {
|
|
925
|
+
line = lines[index].trim();
|
|
926
|
+
if (!line) {
|
|
927
|
+
continue;
|
|
928
|
+
}
|
|
929
|
+
line = line.split("/");
|
|
930
|
+
word = line[0];
|
|
931
|
+
forbidden = word.charAt(0) === "*";
|
|
932
|
+
if (forbidden) {
|
|
933
|
+
word = word.slice(1);
|
|
934
|
+
}
|
|
935
|
+
self.add(word, line[1]);
|
|
936
|
+
if (forbidden) {
|
|
937
|
+
self.data[word].push(flag);
|
|
938
|
+
}
|
|
1302
939
|
}
|
|
1303
|
-
return
|
|
1304
|
-
}
|
|
1305
|
-
function exact(value) {
|
|
1306
|
-
return value === value.toLowerCase() ? "l" : value === value.toUpperCase() ? "u" : null;
|
|
940
|
+
return self;
|
|
1307
941
|
}
|
|
1308
942
|
}
|
|
1309
943
|
});
|
|
1310
944
|
|
|
1311
|
-
// node_modules/nspell/lib/
|
|
1312
|
-
var
|
|
1313
|
-
"node_modules/nspell/lib/
|
|
945
|
+
// node_modules/nspell/lib/index.js
|
|
946
|
+
var require_lib = __commonJS({
|
|
947
|
+
"node_modules/nspell/lib/index.js"(exports, module) {
|
|
1314
948
|
"use strict";
|
|
1315
|
-
var
|
|
1316
|
-
var
|
|
1317
|
-
|
|
1318
|
-
var
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
var
|
|
1329
|
-
var
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
var position;
|
|
1333
|
-
var count;
|
|
1334
|
-
var otherOffset;
|
|
1335
|
-
var otherCharacter;
|
|
1336
|
-
var character;
|
|
1337
|
-
var group;
|
|
1338
|
-
var before;
|
|
1339
|
-
var after;
|
|
1340
|
-
var upper;
|
|
1341
|
-
var insensitive;
|
|
1342
|
-
var firstLevel;
|
|
1343
|
-
var previous;
|
|
1344
|
-
var next;
|
|
1345
|
-
var nextCharacter;
|
|
1346
|
-
var max;
|
|
1347
|
-
var distance;
|
|
1348
|
-
var size;
|
|
1349
|
-
var normalized;
|
|
1350
|
-
var suggestion;
|
|
1351
|
-
var currentCase;
|
|
1352
|
-
value = normalize(value.trim(), self.conversion.in);
|
|
1353
|
-
if (!value || self.correct(value)) {
|
|
1354
|
-
return [];
|
|
1355
|
-
}
|
|
1356
|
-
currentCase = casing(value);
|
|
1357
|
-
index = -1;
|
|
1358
|
-
while (++index < self.replacementTable.length) {
|
|
1359
|
-
replacement = self.replacementTable[index];
|
|
1360
|
-
offset = value.indexOf(replacement[0]);
|
|
1361
|
-
while (offset > -1) {
|
|
1362
|
-
edits.push(value.replace(replacement[0], replacement[1]));
|
|
1363
|
-
offset = value.indexOf(replacement[0], offset + 1);
|
|
1364
|
-
}
|
|
1365
|
-
}
|
|
1366
|
-
index = -1;
|
|
1367
|
-
while (++index < value.length) {
|
|
1368
|
-
character = value.charAt(index);
|
|
1369
|
-
before = value.slice(0, index);
|
|
1370
|
-
after = value.slice(index + 1);
|
|
1371
|
-
insensitive = character.toLowerCase();
|
|
1372
|
-
upper = insensitive !== character;
|
|
1373
|
-
charAdded = {};
|
|
1374
|
-
offset = -1;
|
|
1375
|
-
while (++offset < self.flags.KEY.length) {
|
|
1376
|
-
group = self.flags.KEY[offset];
|
|
1377
|
-
position = group.indexOf(insensitive);
|
|
1378
|
-
if (position < 0) {
|
|
1379
|
-
continue;
|
|
1380
|
-
}
|
|
1381
|
-
otherOffset = -1;
|
|
1382
|
-
while (++otherOffset < group.length) {
|
|
1383
|
-
if (otherOffset !== position) {
|
|
1384
|
-
otherCharacter = group.charAt(otherOffset);
|
|
1385
|
-
if (charAdded[otherCharacter]) {
|
|
1386
|
-
continue;
|
|
1387
|
-
}
|
|
1388
|
-
charAdded[otherCharacter] = true;
|
|
1389
|
-
if (upper) {
|
|
1390
|
-
otherCharacter = otherCharacter.toUpperCase();
|
|
1391
|
-
}
|
|
1392
|
-
edits.push(before + otherCharacter + after);
|
|
1393
|
-
}
|
|
1394
|
-
}
|
|
1395
|
-
}
|
|
949
|
+
var buffer = require_is_buffer();
|
|
950
|
+
var affix = require_affix();
|
|
951
|
+
module.exports = NSpell3;
|
|
952
|
+
var proto = NSpell3.prototype;
|
|
953
|
+
proto.correct = require_correct();
|
|
954
|
+
proto.suggest = require_suggest();
|
|
955
|
+
proto.spell = require_spell();
|
|
956
|
+
proto.add = require_add2();
|
|
957
|
+
proto.remove = require_remove();
|
|
958
|
+
proto.wordCharacters = require_word_characters();
|
|
959
|
+
proto.dictionary = require_dictionary2();
|
|
960
|
+
proto.personal = require_personal();
|
|
961
|
+
function NSpell3(aff, dic) {
|
|
962
|
+
var index = -1;
|
|
963
|
+
var dictionaries;
|
|
964
|
+
if (!(this instanceof NSpell3)) {
|
|
965
|
+
return new NSpell3(aff, dic);
|
|
1396
966
|
}
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
max = 1;
|
|
1401
|
-
distance = 0;
|
|
1402
|
-
while (++index < value.length) {
|
|
1403
|
-
character = nextCharacter;
|
|
1404
|
-
nextCharacter = value.charAt(index + 1);
|
|
1405
|
-
before = value.slice(0, index);
|
|
1406
|
-
replacement = character === nextCharacter ? "" : character + character;
|
|
1407
|
-
offset = -1;
|
|
1408
|
-
count = values.length;
|
|
1409
|
-
while (++offset < count) {
|
|
1410
|
-
if (offset <= max) {
|
|
1411
|
-
values.push(values[offset] + replacement);
|
|
1412
|
-
}
|
|
1413
|
-
values[offset] += character;
|
|
1414
|
-
}
|
|
1415
|
-
if (++distance < 3) {
|
|
1416
|
-
max = values.length;
|
|
967
|
+
if (typeof aff === "string" || buffer(aff)) {
|
|
968
|
+
if (typeof dic === "string" || buffer(dic)) {
|
|
969
|
+
dictionaries = [{ dic }];
|
|
1417
970
|
}
|
|
1418
|
-
}
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
values.push(replacement);
|
|
1428
|
-
}
|
|
1429
|
-
memory = {
|
|
1430
|
-
state: {},
|
|
1431
|
-
weighted,
|
|
1432
|
-
suggestions
|
|
1433
|
-
};
|
|
1434
|
-
firstLevel = generate(self, memory, values, edits);
|
|
1435
|
-
previous = 0;
|
|
1436
|
-
max = Math.min(firstLevel.length, Math.pow(Math.max(15 - value.length, 3), 3));
|
|
1437
|
-
size = Math.max(Math.pow(10 - value.length, 3), 1);
|
|
1438
|
-
while (!suggestions.length && previous < max) {
|
|
1439
|
-
next = previous + size;
|
|
1440
|
-
generate(self, memory, firstLevel.slice(previous, next));
|
|
1441
|
-
previous = next;
|
|
1442
|
-
}
|
|
1443
|
-
suggestions.sort(sort);
|
|
1444
|
-
values = [];
|
|
1445
|
-
normalized = [];
|
|
1446
|
-
index = -1;
|
|
1447
|
-
while (++index < suggestions.length) {
|
|
1448
|
-
suggestion = normalize(suggestions[index], self.conversion.out);
|
|
1449
|
-
replacement = suggestion.toLowerCase();
|
|
1450
|
-
if (normalized.indexOf(replacement) < 0) {
|
|
1451
|
-
values.push(suggestion);
|
|
1452
|
-
normalized.push(replacement);
|
|
971
|
+
} else if (aff) {
|
|
972
|
+
if ("length" in aff) {
|
|
973
|
+
dictionaries = aff;
|
|
974
|
+
aff = aff[0] && aff[0].aff;
|
|
975
|
+
} else {
|
|
976
|
+
if (aff.dic) {
|
|
977
|
+
dictionaries = [aff];
|
|
978
|
+
}
|
|
979
|
+
aff = aff.aff;
|
|
1453
980
|
}
|
|
1454
981
|
}
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
return sortWeight(a, b) || sortCasing(a, b) || sortAlpha(a, b);
|
|
982
|
+
if (!aff) {
|
|
983
|
+
throw new Error("Missing `aff` in dictionary");
|
|
1458
984
|
}
|
|
1459
|
-
|
|
1460
|
-
|
|
985
|
+
aff = affix(aff);
|
|
986
|
+
this.data = /* @__PURE__ */ Object.create(null);
|
|
987
|
+
this.compoundRuleCodes = aff.compoundRuleCodes;
|
|
988
|
+
this.replacementTable = aff.replacementTable;
|
|
989
|
+
this.conversion = aff.conversion;
|
|
990
|
+
this.compoundRules = aff.compoundRules;
|
|
991
|
+
this.rules = aff.rules;
|
|
992
|
+
this.flags = aff.flags;
|
|
993
|
+
if (dictionaries) {
|
|
994
|
+
while (++index < dictionaries.length) {
|
|
995
|
+
if (dictionaries[index].dic) {
|
|
996
|
+
this.dictionary(dictionaries[index].dic);
|
|
997
|
+
}
|
|
998
|
+
}
|
|
1461
999
|
}
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
});
|
|
1003
|
+
|
|
1004
|
+
// client/lib/api-client.js
|
|
1005
|
+
var api_client_exports = {};
|
|
1006
|
+
__export(api_client_exports, {
|
|
1007
|
+
abortMessageListRequests: () => abortMessageListRequests,
|
|
1008
|
+
addContact: () => addContact,
|
|
1009
|
+
addPreferredContact: () => addPreferredContact,
|
|
1010
|
+
addToDenylist: () => addToDenylist,
|
|
1011
|
+
addUserDictWord: () => addUserDictWord,
|
|
1012
|
+
addUserDictWords: () => addUserDictWords,
|
|
1013
|
+
aiTransform: () => aiTransform,
|
|
1014
|
+
allowRemoteContent: () => allowRemoteContent,
|
|
1015
|
+
autocomplete: () => autocomplete,
|
|
1016
|
+
cancelQueuedOutgoing: () => cancelQueuedOutgoing,
|
|
1017
|
+
cancelServerSearch: () => cancelServerSearch,
|
|
1018
|
+
closeWordEdit: () => closeWordEdit,
|
|
1019
|
+
connectEvents: () => connectEvents,
|
|
1020
|
+
connectWebSocket: () => connectWebSocket,
|
|
1021
|
+
consumePendingMailto: () => consumePendingMailto,
|
|
1022
|
+
createCalendarEvent: () => createCalendarEvent,
|
|
1023
|
+
createFolder: () => createFolder,
|
|
1024
|
+
createTask: () => createTask,
|
|
1025
|
+
deleteCalendarEvent: () => deleteCalendarEvent,
|
|
1026
|
+
deleteContact: () => deleteContact,
|
|
1027
|
+
deleteDraft: () => deleteDraft,
|
|
1028
|
+
deleteFolder: () => deleteFolder,
|
|
1029
|
+
deleteMessage: () => deleteMessage,
|
|
1030
|
+
deleteMessages: () => deleteMessages,
|
|
1031
|
+
deleteTask: () => deleteTask,
|
|
1032
|
+
drainStoreSync: () => drainStoreSync,
|
|
1033
|
+
emptyFolder: () => emptyFolder,
|
|
1034
|
+
flagSenderOrDomain: () => flagSenderOrDomain,
|
|
1035
|
+
formatJsonc: () => formatJsonc,
|
|
1036
|
+
getAccounts: () => getAccounts,
|
|
1037
|
+
getAttachment: () => getAttachment,
|
|
1038
|
+
getAutocompleteSettings: () => getAutocompleteSettings,
|
|
1039
|
+
getCalendarEvents: () => getCalendarEvents,
|
|
1040
|
+
getCalendars: () => getCalendars,
|
|
1041
|
+
getDeviceAccounts: () => getDeviceAccounts,
|
|
1042
|
+
getDiagnostics: () => getDiagnostics,
|
|
1043
|
+
getFolders: () => getFolders,
|
|
1044
|
+
getMessage: () => getMessage,
|
|
1045
|
+
getMessages: () => getMessages,
|
|
1046
|
+
getOutboxStatus: () => getOutboxStatus,
|
|
1047
|
+
getPrimaryAccount: () => getPrimaryAccount,
|
|
1048
|
+
getPriorityLists: () => getPriorityLists,
|
|
1049
|
+
getSettings: () => getSettings,
|
|
1050
|
+
getSyncPending: () => getSyncPending,
|
|
1051
|
+
getTasks: () => getTasks,
|
|
1052
|
+
getThreadMessages: () => getThreadMessages,
|
|
1053
|
+
getUnifiedInbox: () => getUnifiedInbox,
|
|
1054
|
+
getUserDict: () => getUserDict,
|
|
1055
|
+
getVersion: () => getVersion,
|
|
1056
|
+
hasBccHistoryTo: () => hasBccHistoryTo,
|
|
1057
|
+
hasCcHistoryTo: () => hasCcHistoryTo,
|
|
1058
|
+
installConsoleCapture: () => installConsoleCapture,
|
|
1059
|
+
listContacts: () => listContacts,
|
|
1060
|
+
listQueuedOutgoing: () => listQueuedOutgoing,
|
|
1061
|
+
logClientEvent: () => logClientEvent,
|
|
1062
|
+
markAsSpamMessages: () => markAsSpamMessages,
|
|
1063
|
+
markFolderRead: () => markFolderRead,
|
|
1064
|
+
moveFolderToTrash: () => moveFolderToTrash,
|
|
1065
|
+
moveMessage: () => moveMessage,
|
|
1066
|
+
moveMessages: () => moveMessages,
|
|
1067
|
+
onEvent: () => onEvent,
|
|
1068
|
+
onWsEvent: () => onWsEvent,
|
|
1069
|
+
openAttachment: () => openAttachment,
|
|
1070
|
+
openInTextEditor: () => openInTextEditor,
|
|
1071
|
+
openInWord: () => openInWord,
|
|
1072
|
+
openLocalPath: () => openLocalPath,
|
|
1073
|
+
readConfigHelp: () => readConfigHelp,
|
|
1074
|
+
readJsoncFile: () => readJsoncFile,
|
|
1075
|
+
reauthGoogleScopes: () => reauthGoogleScopes,
|
|
1076
|
+
reauthenticate: () => reauthenticate,
|
|
1077
|
+
recordSpamReport: () => recordSpamReport,
|
|
1078
|
+
removeUserDictWord: () => removeUserDictWord,
|
|
1079
|
+
renameFolder: () => renameFolder,
|
|
1080
|
+
repairAccounts: () => repairAccounts,
|
|
1081
|
+
restartServer: () => restartServer,
|
|
1082
|
+
saveAutocompleteSettings: () => saveAutocompleteSettings,
|
|
1083
|
+
saveDraft: () => saveDraft,
|
|
1084
|
+
saveSettings: () => saveSettings,
|
|
1085
|
+
searchContacts: () => searchContacts,
|
|
1086
|
+
searchMessages: () => searchMessages,
|
|
1087
|
+
sendMessage: () => sendMessage,
|
|
1088
|
+
setPriorityDomain: () => setPriorityDomain,
|
|
1089
|
+
setPrioritySender: () => setPrioritySender,
|
|
1090
|
+
setupAccount: () => setupAccount,
|
|
1091
|
+
showReminderPopup: () => showReminderPopup,
|
|
1092
|
+
subscribeStore: () => subscribeStore,
|
|
1093
|
+
syncAccount: () => syncAccount,
|
|
1094
|
+
triggerSync: () => triggerSync,
|
|
1095
|
+
undeleteMessage: () => undeleteMessage,
|
|
1096
|
+
unsubscribeOneClick: () => unsubscribeOneClick,
|
|
1097
|
+
updateCalendarEvent: () => updateCalendarEvent,
|
|
1098
|
+
updateFlags: () => updateFlags,
|
|
1099
|
+
updateTask: () => updateTask,
|
|
1100
|
+
upsertContact: () => upsertContact,
|
|
1101
|
+
writeJsoncFile: () => writeJsoncFile
|
|
1102
|
+
});
|
|
1103
|
+
function getIpc() {
|
|
1104
|
+
if (typeof mailxapi !== "undefined" && mailxapi?.isApp)
|
|
1105
|
+
return mailxapi;
|
|
1106
|
+
if (window.opener?.mailxapi?.isApp)
|
|
1107
|
+
return window.opener.mailxapi;
|
|
1108
|
+
if (window.parent?.mailxapi?.isApp)
|
|
1109
|
+
return window.parent.mailxapi;
|
|
1110
|
+
return null;
|
|
1111
|
+
}
|
|
1112
|
+
function buildRelayBridge() {
|
|
1113
|
+
const pending = /* @__PURE__ */ new Map();
|
|
1114
|
+
window.addEventListener("message", (ev) => {
|
|
1115
|
+
if (!ev.data || ev.data.type !== "mailx-ipc-result" || !ev.data.id)
|
|
1116
|
+
return;
|
|
1117
|
+
const entry = pending.get(ev.data.id);
|
|
1118
|
+
if (!entry)
|
|
1119
|
+
return;
|
|
1120
|
+
pending.delete(ev.data.id);
|
|
1121
|
+
clearTimeout(entry.timer);
|
|
1122
|
+
if (ev.data.ok)
|
|
1123
|
+
entry.resolve(ev.data.result);
|
|
1124
|
+
else
|
|
1125
|
+
entry.reject(new Error(ev.data.error || "parent-relay ipc error"));
|
|
1126
|
+
});
|
|
1127
|
+
const call = (method, args) => {
|
|
1128
|
+
const id = `ipc-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
1129
|
+
return new Promise((resolve, reject) => {
|
|
1130
|
+
const timer = setTimeout(() => {
|
|
1131
|
+
pending.delete(id);
|
|
1132
|
+
reject(new Error(`parent-relay timeout: ${method}`));
|
|
1133
|
+
}, 12e4);
|
|
1134
|
+
pending.set(id, { resolve, reject, timer });
|
|
1135
|
+
try {
|
|
1136
|
+
window.parent.postMessage({ type: "mailx-ipc", id, method, args }, "*");
|
|
1137
|
+
} catch (e) {
|
|
1138
|
+
clearTimeout(timer);
|
|
1139
|
+
pending.delete(id);
|
|
1140
|
+
reject(e);
|
|
1466
1141
|
}
|
|
1467
|
-
|
|
1468
|
-
|
|
1142
|
+
});
|
|
1143
|
+
};
|
|
1144
|
+
return new Proxy({}, {
|
|
1145
|
+
get(_t, prop) {
|
|
1146
|
+
if (prop === "isApp")
|
|
1147
|
+
return true;
|
|
1148
|
+
if (prop === "platform")
|
|
1149
|
+
return window.parent?.mailxapi?.platform || "webview2";
|
|
1150
|
+
if (prop === "onEvent") {
|
|
1151
|
+
return (handler) => window.parent?.mailxapi?.onEvent?.(handler);
|
|
1152
|
+
}
|
|
1153
|
+
return (...args) => call(prop, args);
|
|
1154
|
+
}
|
|
1155
|
+
});
|
|
1156
|
+
}
|
|
1157
|
+
function ipc() {
|
|
1158
|
+
const inIframe = window.parent && window.parent !== window;
|
|
1159
|
+
if (inIframe && window.parent?.mailxapi?.isApp) {
|
|
1160
|
+
if (!cachedRelayBridge)
|
|
1161
|
+
cachedRelayBridge = buildRelayBridge();
|
|
1162
|
+
return cachedRelayBridge;
|
|
1163
|
+
}
|
|
1164
|
+
const bridge = getIpc();
|
|
1165
|
+
if (!bridge)
|
|
1166
|
+
throw new Error("IPC bridge not available");
|
|
1167
|
+
return bridge;
|
|
1168
|
+
}
|
|
1169
|
+
function abortMessageListRequests() {
|
|
1170
|
+
if (messageListAbort) {
|
|
1171
|
+
messageListAbort.abort();
|
|
1172
|
+
messageListAbort = null;
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
function getAccounts() {
|
|
1176
|
+
return ipc().getAccounts();
|
|
1177
|
+
}
|
|
1178
|
+
function getFolders(accountId) {
|
|
1179
|
+
return ipc().getFolders(accountId);
|
|
1180
|
+
}
|
|
1181
|
+
function getMessages(accountId, folderId, page = 1, pageSize = 50, flaggedOnly = false, sort, sortDir) {
|
|
1182
|
+
abortMessageListRequests();
|
|
1183
|
+
return ipc().getMessages(accountId, folderId, page, pageSize, sort, sortDir, void 0, flaggedOnly);
|
|
1184
|
+
}
|
|
1185
|
+
function getUnifiedInbox(page = 1, pageSize = 50) {
|
|
1186
|
+
abortMessageListRequests();
|
|
1187
|
+
return ipc().getUnifiedInbox(page, pageSize);
|
|
1188
|
+
}
|
|
1189
|
+
function searchMessages(query, page = 1, pageSize = 50, scope = "all", accountId = "", folderId = 0, includeTrashSpam = false) {
|
|
1190
|
+
return ipc().searchMessages(query, page, pageSize, scope, accountId, folderId, includeTrashSpam);
|
|
1191
|
+
}
|
|
1192
|
+
function cancelServerSearch() {
|
|
1193
|
+
return ipc().cancelServerSearch?.();
|
|
1194
|
+
}
|
|
1195
|
+
function getMessage(accountId, uid, allowRemote = false, folderId) {
|
|
1196
|
+
return ipc().getMessage(accountId, uid, allowRemote, folderId);
|
|
1197
|
+
}
|
|
1198
|
+
function updateFlags(accountId, uid, flags) {
|
|
1199
|
+
return ipc().updateFlags(accountId, uid, flags);
|
|
1200
|
+
}
|
|
1201
|
+
function triggerSync() {
|
|
1202
|
+
return ipc().syncAll();
|
|
1203
|
+
}
|
|
1204
|
+
function syncAccount(accountId) {
|
|
1205
|
+
return ipc().syncAccount(accountId);
|
|
1206
|
+
}
|
|
1207
|
+
function reauthenticate(accountId) {
|
|
1208
|
+
return ipc().reauthenticate(accountId);
|
|
1209
|
+
}
|
|
1210
|
+
function reauthGoogleScopes() {
|
|
1211
|
+
return ipc().reauthGoogleScopes();
|
|
1212
|
+
}
|
|
1213
|
+
function getSyncPending() {
|
|
1214
|
+
return ipc().getSyncPending();
|
|
1215
|
+
}
|
|
1216
|
+
function getDiagnostics() {
|
|
1217
|
+
return ipc().getDiagnostics?.() ?? Promise.resolve([]);
|
|
1218
|
+
}
|
|
1219
|
+
function getPrimaryAccount(feature) {
|
|
1220
|
+
return ipc().getPrimaryAccount?.(feature) ?? Promise.resolve(null);
|
|
1221
|
+
}
|
|
1222
|
+
function getCalendarEvents(fromMs, toMs) {
|
|
1223
|
+
return ipc().getCalendarEvents?.(fromMs, toMs) ?? Promise.resolve([]);
|
|
1224
|
+
}
|
|
1225
|
+
function getCalendars() {
|
|
1226
|
+
return ipc().getCalendars?.() ?? Promise.resolve([]);
|
|
1227
|
+
}
|
|
1228
|
+
function createCalendarEvent(ev) {
|
|
1229
|
+
return ipc().createCalendarEvent?.(ev);
|
|
1230
|
+
}
|
|
1231
|
+
function updateCalendarEvent(uuid, patch) {
|
|
1232
|
+
return ipc().updateCalendarEvent?.(uuid, patch);
|
|
1233
|
+
}
|
|
1234
|
+
function deleteCalendarEvent(uuid) {
|
|
1235
|
+
return ipc().deleteCalendarEvent?.(uuid);
|
|
1236
|
+
}
|
|
1237
|
+
function getTasks(includeCompleted = false) {
|
|
1238
|
+
return ipc().getTasks?.(includeCompleted) ?? Promise.resolve([]);
|
|
1239
|
+
}
|
|
1240
|
+
function createTask(t) {
|
|
1241
|
+
return ipc().createTask?.(t);
|
|
1242
|
+
}
|
|
1243
|
+
function updateTask(uuid, patch) {
|
|
1244
|
+
return ipc().updateTask?.(uuid, patch);
|
|
1245
|
+
}
|
|
1246
|
+
function deleteTask(uuid) {
|
|
1247
|
+
return ipc().deleteTask?.(uuid);
|
|
1248
|
+
}
|
|
1249
|
+
function drainStoreSync() {
|
|
1250
|
+
return ipc().drainStoreSync?.();
|
|
1251
|
+
}
|
|
1252
|
+
function recordSpamReport(accountId, uid, folderId) {
|
|
1253
|
+
return ipc().recordSpamReport?.(accountId, uid, folderId);
|
|
1254
|
+
}
|
|
1255
|
+
function getOutboxStatus() {
|
|
1256
|
+
return ipc().getOutboxStatus();
|
|
1257
|
+
}
|
|
1258
|
+
function listQueuedOutgoing() {
|
|
1259
|
+
return ipc().listQueuedOutgoing();
|
|
1260
|
+
}
|
|
1261
|
+
function cancelQueuedOutgoing(p) {
|
|
1262
|
+
return ipc().cancelQueuedOutgoing(p);
|
|
1263
|
+
}
|
|
1264
|
+
function searchContacts(query) {
|
|
1265
|
+
return ipc().searchContacts(query);
|
|
1266
|
+
}
|
|
1267
|
+
function hasCcHistoryTo(email) {
|
|
1268
|
+
return ipc().hasCcHistoryTo(email);
|
|
1269
|
+
}
|
|
1270
|
+
function hasBccHistoryTo(email) {
|
|
1271
|
+
return ipc().hasBccHistoryTo(email);
|
|
1272
|
+
}
|
|
1273
|
+
function listContacts(query, page = 1, pageSize = 100) {
|
|
1274
|
+
return ipc().listContacts(query, page, pageSize);
|
|
1275
|
+
}
|
|
1276
|
+
function upsertContact(name, email) {
|
|
1277
|
+
return ipc().upsertContact(name, email);
|
|
1278
|
+
}
|
|
1279
|
+
function deleteContact(email) {
|
|
1280
|
+
return ipc().deleteContact(email);
|
|
1281
|
+
}
|
|
1282
|
+
function addPreferredContact(entry) {
|
|
1283
|
+
return ipc().addPreferredContact(entry.name, entry.email, entry.source, entry.organization);
|
|
1284
|
+
}
|
|
1285
|
+
function getPriorityLists() {
|
|
1286
|
+
return ipc().getPriorityLists();
|
|
1287
|
+
}
|
|
1288
|
+
function setPrioritySender(email, value, name) {
|
|
1289
|
+
return ipc().setPrioritySender(email, value, name);
|
|
1290
|
+
}
|
|
1291
|
+
function setPriorityDomain(domain, value) {
|
|
1292
|
+
return ipc().setPriorityDomain(domain, value);
|
|
1293
|
+
}
|
|
1294
|
+
function addToDenylist(email) {
|
|
1295
|
+
return ipc().addToDenylist(email);
|
|
1296
|
+
}
|
|
1297
|
+
function openLocalPath(which) {
|
|
1298
|
+
return ipc().openLocalPath(which);
|
|
1299
|
+
}
|
|
1300
|
+
function openInTextEditor(path) {
|
|
1301
|
+
return ipc().openInTextEditor?.(path) ?? Promise.resolve({ ok: false, opener: "none", reason: "no host" });
|
|
1302
|
+
}
|
|
1303
|
+
function allowRemoteContent(type, value) {
|
|
1304
|
+
return ipc().allowRemoteContent(type, value);
|
|
1305
|
+
}
|
|
1306
|
+
function getUserDict() {
|
|
1307
|
+
return ipc().getUserDict?.() ?? Promise.resolve([]);
|
|
1308
|
+
}
|
|
1309
|
+
function addUserDictWord(word) {
|
|
1310
|
+
return ipc().addUserDictWord?.(word) ?? Promise.resolve([]);
|
|
1311
|
+
}
|
|
1312
|
+
function addUserDictWords(words) {
|
|
1313
|
+
return ipc().addUserDictWords?.(words) ?? Promise.resolve([]);
|
|
1314
|
+
}
|
|
1315
|
+
function removeUserDictWord(word) {
|
|
1316
|
+
return ipc().removeUserDictWord?.(word) ?? Promise.resolve([]);
|
|
1317
|
+
}
|
|
1318
|
+
function flagSenderOrDomain(type, value) {
|
|
1319
|
+
return ipc().flagSenderOrDomain?.(type, value) ?? Promise.resolve({ flagged: false });
|
|
1320
|
+
}
|
|
1321
|
+
function deleteMessage(accountId, uid) {
|
|
1322
|
+
return ipc().deleteMessage?.(accountId, uid);
|
|
1323
|
+
}
|
|
1324
|
+
function deleteMessages(accountId, uids) {
|
|
1325
|
+
if (uids.length === 1)
|
|
1326
|
+
return deleteMessage(accountId, uids[0]);
|
|
1327
|
+
return ipc().deleteMessages?.(accountId, uids);
|
|
1328
|
+
}
|
|
1329
|
+
function moveMessages(accountId, uids, targetFolderId, targetAccountId) {
|
|
1330
|
+
if (uids.length === 1)
|
|
1331
|
+
return moveMessage(accountId, uids[0], targetFolderId, targetAccountId);
|
|
1332
|
+
return ipc().moveMessages?.(accountId, uids, targetFolderId, targetAccountId);
|
|
1333
|
+
}
|
|
1334
|
+
function markAsSpamMessages(accountId, uids) {
|
|
1335
|
+
return ipc().markAsSpamMessages?.(accountId, uids);
|
|
1336
|
+
}
|
|
1337
|
+
function undeleteMessage(accountId, uid, folderId) {
|
|
1338
|
+
return ipc().undeleteMessage?.(accountId, uid, folderId);
|
|
1339
|
+
}
|
|
1340
|
+
function moveMessage(accountId, uid, targetFolderId, targetAccountId) {
|
|
1341
|
+
return ipc().moveMessage?.(accountId, uid, targetFolderId, targetAccountId);
|
|
1342
|
+
}
|
|
1343
|
+
function restartServer() {
|
|
1344
|
+
return ipc().restart?.();
|
|
1345
|
+
}
|
|
1346
|
+
function markFolderRead(accountId, folderId) {
|
|
1347
|
+
return ipc().markFolderRead?.(accountId, folderId);
|
|
1348
|
+
}
|
|
1349
|
+
function createFolder(accountId, parentPath, name) {
|
|
1350
|
+
return ipc().createFolder?.(accountId, parentPath, name);
|
|
1351
|
+
}
|
|
1352
|
+
function renameFolder(accountId, folderId, newName) {
|
|
1353
|
+
return ipc().renameFolder?.(accountId, folderId, newName);
|
|
1354
|
+
}
|
|
1355
|
+
function deleteFolder(accountId, folderId) {
|
|
1356
|
+
return ipc().deleteFolder?.(accountId, folderId);
|
|
1357
|
+
}
|
|
1358
|
+
function moveFolderToTrash(accountId, folderId) {
|
|
1359
|
+
return ipc().moveFolderToTrash?.(accountId, folderId);
|
|
1360
|
+
}
|
|
1361
|
+
function emptyFolder(accountId, folderId) {
|
|
1362
|
+
return ipc().emptyFolder?.(accountId, folderId);
|
|
1363
|
+
}
|
|
1364
|
+
function installConsoleCapture() {
|
|
1365
|
+
const g = globalThis;
|
|
1366
|
+
if (g.__mailxConsoleCaptureInstalled)
|
|
1367
|
+
return;
|
|
1368
|
+
g.__mailxConsoleCaptureInstalled = true;
|
|
1369
|
+
const orig = {
|
|
1370
|
+
log: console.log.bind(console),
|
|
1371
|
+
info: console.info.bind(console),
|
|
1372
|
+
warn: console.warn.bind(console),
|
|
1373
|
+
error: console.error.bind(console),
|
|
1374
|
+
debug: console.debug.bind(console)
|
|
1375
|
+
};
|
|
1376
|
+
g._origConsole = orig;
|
|
1377
|
+
const serialize = (v) => {
|
|
1378
|
+
if (v == null)
|
|
1379
|
+
return v;
|
|
1380
|
+
const t = typeof v;
|
|
1381
|
+
if (t === "string" || t === "number" || t === "boolean")
|
|
1382
|
+
return v;
|
|
1383
|
+
if (v instanceof Error)
|
|
1384
|
+
return { __err: true, message: v.message, stack: v.stack };
|
|
1385
|
+
try {
|
|
1386
|
+
const s = JSON.stringify(v);
|
|
1387
|
+
return s.length > 4096 ? s.slice(0, 4096) + "\u2026[truncated]" : JSON.parse(s);
|
|
1388
|
+
} catch {
|
|
1389
|
+
try {
|
|
1390
|
+
return String(v);
|
|
1391
|
+
} catch {
|
|
1392
|
+
return "[unserializable]";
|
|
1469
1393
|
}
|
|
1470
1394
|
}
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
nextAfter = nextNextAfter;
|
|
1510
|
-
nextNextAfter = nextAfter.slice(1);
|
|
1511
|
-
character = nextCharacter;
|
|
1512
|
-
nextCharacter = word.charAt(position + 1);
|
|
1513
|
-
upper = nextUpper;
|
|
1514
|
-
if (nextCharacter) {
|
|
1515
|
-
nextUpper = nextCharacter.toLowerCase() !== nextCharacter;
|
|
1516
|
-
}
|
|
1517
|
-
if (nextAfter && upper !== nextUpper) {
|
|
1518
|
-
check(before + switchCase(nextAfter));
|
|
1519
|
-
check(
|
|
1520
|
-
before + switchCase(nextCharacter) + switchCase(character) + nextNextAfter
|
|
1521
|
-
);
|
|
1522
|
-
}
|
|
1523
|
-
check(before + nextAfter);
|
|
1524
|
-
if (nextAfter) {
|
|
1525
|
-
check(before + nextCharacter + character + nextNextAfter);
|
|
1526
|
-
}
|
|
1527
|
-
offset = -1;
|
|
1528
|
-
while (++offset < characters.length) {
|
|
1529
|
-
inject = characters[offset];
|
|
1530
|
-
if (upper && inject !== inject.toUpperCase()) {
|
|
1531
|
-
if (currentCase !== "s") {
|
|
1532
|
-
check(before + inject + after);
|
|
1533
|
-
check(before + inject + nextAfter);
|
|
1534
|
-
}
|
|
1535
|
-
inject = inject.toUpperCase();
|
|
1536
|
-
check(before + inject + after);
|
|
1537
|
-
check(before + inject + nextAfter);
|
|
1538
|
-
} else {
|
|
1539
|
-
check(before + inject + after);
|
|
1540
|
-
check(before + inject + nextAfter);
|
|
1541
|
-
}
|
|
1542
|
-
}
|
|
1543
|
-
}
|
|
1544
|
-
}
|
|
1545
|
-
return result;
|
|
1546
|
-
function check(value, double) {
|
|
1547
|
-
var state = memory.state[value];
|
|
1548
|
-
var corrected;
|
|
1549
|
-
if (state !== Boolean(state)) {
|
|
1550
|
-
result.push(value);
|
|
1551
|
-
corrected = form(context, value);
|
|
1552
|
-
state = corrected && !flag(flags, "NOSUGGEST", data[corrected]);
|
|
1553
|
-
memory.state[value] = state;
|
|
1554
|
-
if (state) {
|
|
1555
|
-
memory.weighted[value] = double ? 10 : 0;
|
|
1556
|
-
memory.suggestions.push(value);
|
|
1557
|
-
}
|
|
1558
|
-
}
|
|
1559
|
-
if (state) {
|
|
1560
|
-
memory.weighted[value]++;
|
|
1561
|
-
}
|
|
1395
|
+
};
|
|
1396
|
+
const forward = (level, args) => {
|
|
1397
|
+
try {
|
|
1398
|
+
logClientEvent(`console.${level}`, { args: args.map(serialize) });
|
|
1399
|
+
} catch {
|
|
1400
|
+
}
|
|
1401
|
+
};
|
|
1402
|
+
console.log = (...args) => {
|
|
1403
|
+
forward("log", args);
|
|
1404
|
+
orig.log(...args);
|
|
1405
|
+
};
|
|
1406
|
+
console.info = (...args) => {
|
|
1407
|
+
forward("info", args);
|
|
1408
|
+
orig.info(...args);
|
|
1409
|
+
};
|
|
1410
|
+
console.warn = (...args) => {
|
|
1411
|
+
forward("warn", args);
|
|
1412
|
+
orig.warn(...args);
|
|
1413
|
+
};
|
|
1414
|
+
console.error = (...args) => {
|
|
1415
|
+
forward("error", args);
|
|
1416
|
+
orig.error(...args);
|
|
1417
|
+
};
|
|
1418
|
+
console.debug = (...args) => {
|
|
1419
|
+
forward("debug", args);
|
|
1420
|
+
orig.debug(...args);
|
|
1421
|
+
};
|
|
1422
|
+
try {
|
|
1423
|
+
window.addEventListener("error", (e) => {
|
|
1424
|
+
try {
|
|
1425
|
+
logClientEvent("window.error", {
|
|
1426
|
+
message: e.message,
|
|
1427
|
+
filename: e.filename,
|
|
1428
|
+
lineno: e.lineno,
|
|
1429
|
+
colno: e.colno,
|
|
1430
|
+
stack: e.error?.stack || null
|
|
1431
|
+
});
|
|
1432
|
+
} catch {
|
|
1562
1433
|
}
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1434
|
+
});
|
|
1435
|
+
window.addEventListener("unhandledrejection", (e) => {
|
|
1436
|
+
try {
|
|
1437
|
+
const r = e.reason;
|
|
1438
|
+
logClientEvent("window.unhandledrejection", {
|
|
1439
|
+
message: r?.message || String(r),
|
|
1440
|
+
stack: r?.stack || null
|
|
1441
|
+
});
|
|
1442
|
+
} catch {
|
|
1566
1443
|
}
|
|
1567
|
-
}
|
|
1444
|
+
});
|
|
1445
|
+
} catch {
|
|
1568
1446
|
}
|
|
1569
|
-
}
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
module.exports = spell;
|
|
1578
|
-
function spell(word) {
|
|
1579
|
-
var self = this;
|
|
1580
|
-
var value = form(self, word, true);
|
|
1581
|
-
return {
|
|
1582
|
-
correct: self.correct(word),
|
|
1583
|
-
forbidden: Boolean(
|
|
1584
|
-
value && flag(self.flags, "FORBIDDENWORD", self.data[value])
|
|
1585
|
-
),
|
|
1586
|
-
warn: Boolean(value && flag(self.flags, "WARN", self.data[value]))
|
|
1587
|
-
};
|
|
1447
|
+
}
|
|
1448
|
+
function logClientEvent(tag, data) {
|
|
1449
|
+
let delivered = false;
|
|
1450
|
+
try {
|
|
1451
|
+
const bridge = typeof globalThis.mailxapi !== "undefined" && globalThis.mailxapi?.isApp ? globalThis.mailxapi : window.opener?.mailxapi?.isApp ? window.opener.mailxapi : window.parent?.mailxapi?.isApp ? window.parent.mailxapi : null;
|
|
1452
|
+
if (bridge?.logClientEvent) {
|
|
1453
|
+
bridge.logClientEvent(tag, data);
|
|
1454
|
+
delivered = true;
|
|
1588
1455
|
}
|
|
1456
|
+
} catch {
|
|
1589
1457
|
}
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
var require_apply = __commonJS({
|
|
1594
|
-
"node_modules/nspell/lib/util/apply.js"(exports, module) {
|
|
1595
|
-
"use strict";
|
|
1596
|
-
module.exports = apply;
|
|
1597
|
-
function apply(value, rule, rules, words) {
|
|
1598
|
-
var index = -1;
|
|
1599
|
-
var entry;
|
|
1600
|
-
var next;
|
|
1601
|
-
var continuationRule;
|
|
1602
|
-
var continuation;
|
|
1603
|
-
var position;
|
|
1604
|
-
while (++index < rule.entries.length) {
|
|
1605
|
-
entry = rule.entries[index];
|
|
1606
|
-
continuation = entry.continuation;
|
|
1607
|
-
position = -1;
|
|
1608
|
-
if (!entry.match || entry.match.test(value)) {
|
|
1609
|
-
next = entry.remove ? value.replace(entry.remove, "") : value;
|
|
1610
|
-
next = rule.type === "SFX" ? next + entry.add : entry.add + next;
|
|
1611
|
-
words.push(next);
|
|
1612
|
-
if (continuation && continuation.length) {
|
|
1613
|
-
while (++position < continuation.length) {
|
|
1614
|
-
continuationRule = rules[continuation[position]];
|
|
1615
|
-
if (continuationRule) {
|
|
1616
|
-
apply(next, continuationRule, rules, words);
|
|
1617
|
-
}
|
|
1618
|
-
}
|
|
1619
|
-
}
|
|
1620
|
-
}
|
|
1621
|
-
}
|
|
1622
|
-
return words;
|
|
1458
|
+
try {
|
|
1459
|
+
if (window.parent && window.parent !== window) {
|
|
1460
|
+
window.parent.postMessage({ type: "mailx-trace", tag, data, bridged: delivered }, "*");
|
|
1623
1461
|
}
|
|
1462
|
+
} catch {
|
|
1624
1463
|
}
|
|
1625
|
-
}
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1464
|
+
}
|
|
1465
|
+
function sendMessage(body) {
|
|
1466
|
+
return ipc().sendMessage?.(body);
|
|
1467
|
+
}
|
|
1468
|
+
function saveDraft(body) {
|
|
1469
|
+
return ipc().saveDraft?.(body);
|
|
1470
|
+
}
|
|
1471
|
+
function onEvent(handler) {
|
|
1472
|
+
eventHandlers.push(handler);
|
|
1473
|
+
return () => {
|
|
1474
|
+
const i = eventHandlers.indexOf(handler);
|
|
1475
|
+
if (i >= 0)
|
|
1476
|
+
eventHandlers.splice(i, 1);
|
|
1477
|
+
};
|
|
1478
|
+
}
|
|
1479
|
+
function subscribeStore(topic, handler) {
|
|
1480
|
+
let set = storeSubs.get(topic);
|
|
1481
|
+
if (!set) {
|
|
1482
|
+
set = /* @__PURE__ */ new Set();
|
|
1483
|
+
storeSubs.set(topic, set);
|
|
1484
|
+
}
|
|
1485
|
+
set.add(handler);
|
|
1486
|
+
return () => {
|
|
1487
|
+
const s = storeSubs.get(topic);
|
|
1488
|
+
if (s) {
|
|
1489
|
+
s.delete(handler);
|
|
1490
|
+
if (s.size === 0)
|
|
1491
|
+
storeSubs.delete(topic);
|
|
1646
1492
|
}
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
addRules(dict, word, codes);
|
|
1493
|
+
};
|
|
1494
|
+
}
|
|
1495
|
+
function deliverStore(event) {
|
|
1496
|
+
const exact = storeSubs.get(event.topic);
|
|
1497
|
+
if (exact)
|
|
1498
|
+
for (const h of exact) {
|
|
1499
|
+
try {
|
|
1500
|
+
h(event);
|
|
1501
|
+
} catch (e) {
|
|
1502
|
+
console.error("[store-bus]", e);
|
|
1658
1503
|
}
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
while (++offset < newWords.length) {
|
|
1668
|
-
if (!(newWords[offset] in dict)) {
|
|
1669
|
-
dict[newWords[offset]] = NO_RULES;
|
|
1670
|
-
}
|
|
1671
|
-
if (rule.combineable) {
|
|
1672
|
-
subposition = position;
|
|
1673
|
-
while (++subposition < codes.length) {
|
|
1674
|
-
combined = options.rules[codes[subposition]];
|
|
1675
|
-
if (combined && combined.combineable && rule.type !== combined.type) {
|
|
1676
|
-
otherNewWords = apply(
|
|
1677
|
-
newWords[offset],
|
|
1678
|
-
combined,
|
|
1679
|
-
options.rules,
|
|
1680
|
-
[]
|
|
1681
|
-
);
|
|
1682
|
-
suboffset = -1;
|
|
1683
|
-
while (++suboffset < otherNewWords.length) {
|
|
1684
|
-
if (!(otherNewWords[suboffset] in dict)) {
|
|
1685
|
-
dict[otherNewWords[suboffset]] = NO_RULES;
|
|
1686
|
-
}
|
|
1687
|
-
}
|
|
1688
|
-
}
|
|
1689
|
-
}
|
|
1690
|
-
}
|
|
1691
|
-
}
|
|
1692
|
-
}
|
|
1504
|
+
}
|
|
1505
|
+
const wild = storeSubs.get("*");
|
|
1506
|
+
if (wild)
|
|
1507
|
+
for (const h of wild) {
|
|
1508
|
+
try {
|
|
1509
|
+
h(event);
|
|
1510
|
+
} catch (e) {
|
|
1511
|
+
console.error("[store-bus]", e);
|
|
1693
1512
|
}
|
|
1694
1513
|
}
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1514
|
+
}
|
|
1515
|
+
function connectEvents() {
|
|
1516
|
+
ipc().onEvent((event) => {
|
|
1517
|
+
if (event && event._event === "store")
|
|
1518
|
+
deliverStore(event);
|
|
1519
|
+
for (const h of eventHandlers)
|
|
1520
|
+
h(event);
|
|
1521
|
+
});
|
|
1522
|
+
}
|
|
1523
|
+
function autocomplete(body, signal) {
|
|
1524
|
+
return ipc().autocomplete?.(body);
|
|
1525
|
+
}
|
|
1526
|
+
function getAutocompleteSettings() {
|
|
1527
|
+
return ipc().getAutocompleteSettings?.();
|
|
1528
|
+
}
|
|
1529
|
+
function saveAutocompleteSettings(settings) {
|
|
1530
|
+
return ipc().saveAutocompleteSettings?.(settings);
|
|
1531
|
+
}
|
|
1532
|
+
function getVersion() {
|
|
1533
|
+
return ipc().getVersion();
|
|
1534
|
+
}
|
|
1535
|
+
function getSettings() {
|
|
1536
|
+
return ipc().getSettings();
|
|
1537
|
+
}
|
|
1538
|
+
function saveSettings(settings) {
|
|
1539
|
+
return ipc().saveSettingsData?.(settings);
|
|
1540
|
+
}
|
|
1541
|
+
function repairAccounts() {
|
|
1542
|
+
return ipc().repairAccounts?.();
|
|
1543
|
+
}
|
|
1544
|
+
function deleteDraft(accountId, draftUid2, draftId2) {
|
|
1545
|
+
return ipc().deleteDraft?.(accountId, draftUid2, draftId2);
|
|
1546
|
+
}
|
|
1547
|
+
function addContact(name, email) {
|
|
1548
|
+
return ipc().addContact?.(name, email);
|
|
1549
|
+
}
|
|
1550
|
+
function getThreadMessages(accountId, threadId) {
|
|
1551
|
+
return ipc().getThreadMessages?.(accountId, threadId);
|
|
1552
|
+
}
|
|
1553
|
+
function readJsoncFile(name) {
|
|
1554
|
+
return ipc().readJsoncFile?.(name);
|
|
1555
|
+
}
|
|
1556
|
+
function writeJsoncFile(name, content) {
|
|
1557
|
+
return ipc().writeJsoncFile?.(name, content);
|
|
1558
|
+
}
|
|
1559
|
+
function formatJsonc(content) {
|
|
1560
|
+
return ipc().formatJsonc?.(content);
|
|
1561
|
+
}
|
|
1562
|
+
function readConfigHelp(name) {
|
|
1563
|
+
return ipc().readConfigHelp?.(name) ?? Promise.resolve({ content: "" });
|
|
1564
|
+
}
|
|
1565
|
+
function unsubscribeOneClick(url) {
|
|
1566
|
+
return ipc().unsubscribeOneClick?.(url);
|
|
1567
|
+
}
|
|
1568
|
+
function openInWord(editId, html) {
|
|
1569
|
+
return ipc().openInWord?.(editId, html) ?? Promise.resolve({ ok: false, path: "", opener: "none" });
|
|
1570
|
+
}
|
|
1571
|
+
function closeWordEdit(editId) {
|
|
1572
|
+
return ipc().closeWordEdit?.(editId) ?? Promise.resolve();
|
|
1573
|
+
}
|
|
1574
|
+
function showReminderPopup(opts) {
|
|
1575
|
+
return ipc().showReminderPopup?.(opts) ?? Promise.resolve({ button: "", reason: "no host" });
|
|
1576
|
+
}
|
|
1577
|
+
function consumePendingMailto() {
|
|
1578
|
+
return ipc().consumePendingMailto?.() ?? Promise.resolve(null);
|
|
1579
|
+
}
|
|
1580
|
+
function aiTransform(req) {
|
|
1581
|
+
return ipc().aiTransform?.(req) ?? Promise.resolve({ text: "", reason: "AI not available in this host" });
|
|
1582
|
+
}
|
|
1583
|
+
function setupAccount(name, email, password) {
|
|
1584
|
+
return ipc().setupAccount?.(name, email, password);
|
|
1585
|
+
}
|
|
1586
|
+
async function getAttachment(accountId, uid, attachmentId, folderId) {
|
|
1587
|
+
return ipc().getAttachment(accountId, uid, attachmentId, folderId);
|
|
1588
|
+
}
|
|
1589
|
+
async function openAttachment(accountId, uid, attachmentId, folderId) {
|
|
1590
|
+
const fn = ipc().openAttachment;
|
|
1591
|
+
return fn ? fn(accountId, uid, attachmentId, folderId) : void 0;
|
|
1592
|
+
}
|
|
1593
|
+
async function getDeviceAccounts() {
|
|
1594
|
+
return ipc().getDeviceAccounts?.() ?? [];
|
|
1595
|
+
}
|
|
1596
|
+
var cachedRelayBridge, messageListAbort, eventHandlers, storeSubs, connectWebSocket, onWsEvent;
|
|
1597
|
+
var init_api_client = __esm({
|
|
1598
|
+
"client/lib/api-client.js"() {
|
|
1701
1599
|
"use strict";
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
return self;
|
|
1709
|
-
}
|
|
1600
|
+
cachedRelayBridge = null;
|
|
1601
|
+
messageListAbort = null;
|
|
1602
|
+
eventHandlers = [];
|
|
1603
|
+
storeSubs = /* @__PURE__ */ new Map();
|
|
1604
|
+
connectWebSocket = connectEvents;
|
|
1605
|
+
onWsEvent = onEvent;
|
|
1710
1606
|
}
|
|
1711
1607
|
});
|
|
1712
1608
|
|
|
1713
|
-
//
|
|
1714
|
-
var
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1609
|
+
// client/compose/spellcheck-core.js
|
|
1610
|
+
var spellcheck_core_exports = {};
|
|
1611
|
+
__export(spellcheck_core_exports, {
|
|
1612
|
+
MARKER_ATTR: () => MARKER_ATTR,
|
|
1613
|
+
MIN_WORD_LEN: () => MIN_WORD_LEN,
|
|
1614
|
+
SKIP_TAGS: () => SKIP_TAGS,
|
|
1615
|
+
USER_DICT_KEY: () => USER_DICT_KEY,
|
|
1616
|
+
addToUserDict: () => addToUserDict,
|
|
1617
|
+
buildSuggestionList: () => buildSuggestionList,
|
|
1618
|
+
getSpell: () => getSpell,
|
|
1619
|
+
getWordAtPoint: () => getWordAtPoint,
|
|
1620
|
+
showSuggestionsMenu: () => showSuggestionsMenu
|
|
1724
1621
|
});
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1622
|
+
async function getSpell() {
|
|
1623
|
+
if (spellPromise)
|
|
1624
|
+
return spellPromise;
|
|
1625
|
+
spellPromise = (async () => {
|
|
1626
|
+
const [affRes, dicRes] = await Promise.all([
|
|
1627
|
+
fetch("../lib/dict/en.aff"),
|
|
1628
|
+
fetch("../lib/dict/en.dic")
|
|
1629
|
+
]);
|
|
1630
|
+
if (!affRes.ok || !dicRes.ok) {
|
|
1631
|
+
throw new Error(`spellcheck: dict fetch failed (aff=${affRes.status} dic=${dicRes.status})`);
|
|
1733
1632
|
}
|
|
1734
|
-
|
|
1735
|
-
});
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
var add = require_add();
|
|
1743
|
-
module.exports = parse;
|
|
1744
|
-
var whiteSpaceExpression = /\s/g;
|
|
1745
|
-
function parse(buf, options, dict) {
|
|
1746
|
-
var value = buf.toString("utf8");
|
|
1747
|
-
var last = value.indexOf("\n") + 1;
|
|
1748
|
-
var index = value.indexOf("\n", last);
|
|
1749
|
-
while (index > -1) {
|
|
1750
|
-
if (value.charCodeAt(last) !== 9) {
|
|
1751
|
-
parseLine(value.slice(last, index), options, dict);
|
|
1752
|
-
}
|
|
1753
|
-
last = index + 1;
|
|
1754
|
-
index = value.indexOf("\n", last);
|
|
1755
|
-
}
|
|
1756
|
-
parseLine(value.slice(last), options, dict);
|
|
1633
|
+
const [aff, dic] = await Promise.all([affRes.text(), dicRes.text()]);
|
|
1634
|
+
const sp = new import_nspell.default({ aff, dic });
|
|
1635
|
+
try {
|
|
1636
|
+
const raw = localStorage.getItem(USER_DICT_KEY);
|
|
1637
|
+
if (raw)
|
|
1638
|
+
for (const w of JSON.parse(raw))
|
|
1639
|
+
sp.add(w);
|
|
1640
|
+
} catch {
|
|
1757
1641
|
}
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1642
|
+
getUserDict().then((cloud) => {
|
|
1643
|
+
const cloudArr = Array.isArray(cloud) ? cloud : [];
|
|
1644
|
+
for (const w of cloudArr)
|
|
1645
|
+
sp.add(w);
|
|
1646
|
+
let local = [];
|
|
1647
|
+
try {
|
|
1648
|
+
const raw = localStorage.getItem(USER_DICT_KEY);
|
|
1649
|
+
local = raw ? JSON.parse(raw) : [];
|
|
1650
|
+
} catch {
|
|
1651
|
+
local = [];
|
|
1767
1652
|
}
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
result = whiteSpaceExpression.exec(line);
|
|
1773
|
-
codes = line.slice(slashOffset + 1, result ? result.index : void 0);
|
|
1774
|
-
} else {
|
|
1775
|
-
word = line.slice(0, hashOffset);
|
|
1776
|
-
}
|
|
1777
|
-
} else if (slashOffset > -1) {
|
|
1778
|
-
word = line.slice(0, slashOffset);
|
|
1779
|
-
codes = line.slice(slashOffset + 1);
|
|
1780
|
-
} else {
|
|
1781
|
-
word = line;
|
|
1653
|
+
const cloudSet = new Set(cloudArr);
|
|
1654
|
+
const localOnly = local.filter((w) => !cloudSet.has(w));
|
|
1655
|
+
if (localOnly.length > 0) {
|
|
1656
|
+
addUserDictWords(localOnly).catch((e) => console.error("[spell] reconcile:", e));
|
|
1782
1657
|
}
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1658
|
+
try {
|
|
1659
|
+
const merged = [.../* @__PURE__ */ new Set([...local, ...cloudArr])];
|
|
1660
|
+
localStorage.setItem(USER_DICT_KEY, JSON.stringify(merged));
|
|
1661
|
+
} catch {
|
|
1786
1662
|
}
|
|
1663
|
+
}).catch(() => {
|
|
1664
|
+
});
|
|
1665
|
+
return sp;
|
|
1666
|
+
})();
|
|
1667
|
+
return spellPromise;
|
|
1668
|
+
}
|
|
1669
|
+
function addToUserDict(word, sp) {
|
|
1670
|
+
try {
|
|
1671
|
+
const raw = localStorage.getItem(USER_DICT_KEY);
|
|
1672
|
+
const arr = raw ? JSON.parse(raw) : [];
|
|
1673
|
+
if (!arr.includes(word)) {
|
|
1674
|
+
arr.push(word);
|
|
1675
|
+
localStorage.setItem(USER_DICT_KEY, JSON.stringify(arr));
|
|
1787
1676
|
}
|
|
1677
|
+
} catch {
|
|
1788
1678
|
}
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
var self = this;
|
|
1799
|
-
var index = -1;
|
|
1800
|
-
var rule;
|
|
1801
|
-
var source;
|
|
1802
|
-
var character;
|
|
1803
|
-
var offset;
|
|
1804
|
-
parse(buf, self, self.data);
|
|
1805
|
-
while (++index < self.compoundRules.length) {
|
|
1806
|
-
rule = self.compoundRules[index];
|
|
1807
|
-
source = "";
|
|
1808
|
-
offset = -1;
|
|
1809
|
-
while (++offset < rule.length) {
|
|
1810
|
-
character = rule.charAt(offset);
|
|
1811
|
-
source += self.compoundRuleCodes[character].length ? "(?:" + self.compoundRuleCodes[character].join("|") + ")" : character;
|
|
1812
|
-
}
|
|
1813
|
-
self.compoundRules[index] = new RegExp(source, "i");
|
|
1814
|
-
}
|
|
1815
|
-
return self;
|
|
1679
|
+
sp.add(word);
|
|
1680
|
+
addUserDictWord(word).catch((e) => console.error("[spell] addUserDictWord:", e));
|
|
1681
|
+
}
|
|
1682
|
+
function buildSuggestionList(word, sp) {
|
|
1683
|
+
const transposed = [];
|
|
1684
|
+
for (let i = 0; i < word.length - 1; i++) {
|
|
1685
|
+
const swapped = word.slice(0, i) + word[i + 1] + word[i] + word.slice(i + 2);
|
|
1686
|
+
if (swapped !== word && sp.correct(swapped) && !transposed.includes(swapped)) {
|
|
1687
|
+
transposed.push(swapped);
|
|
1816
1688
|
}
|
|
1817
1689
|
}
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1690
|
+
const nspellSugs = sp.suggest(word);
|
|
1691
|
+
const sugs = [];
|
|
1692
|
+
for (const s of [...transposed, ...nspellSugs]) {
|
|
1693
|
+
if (!sugs.includes(s))
|
|
1694
|
+
sugs.push(s);
|
|
1695
|
+
if (sugs.length >= 7)
|
|
1696
|
+
break;
|
|
1697
|
+
}
|
|
1698
|
+
return sugs;
|
|
1699
|
+
}
|
|
1700
|
+
function showSuggestionsMenu(parentDoc, x, y, items, extraDismissDocs = []) {
|
|
1701
|
+
parentDoc.getElementById("mailx-spell-menu")?.remove();
|
|
1702
|
+
const menu = parentDoc.createElement("div");
|
|
1703
|
+
menu.id = "mailx-spell-menu";
|
|
1704
|
+
menu.style.cssText = `
|
|
1705
|
+
position: fixed;
|
|
1706
|
+
left: ${x}px; top: ${y}px;
|
|
1707
|
+
z-index: 10000;
|
|
1708
|
+
background: var(--color-bg, #fff);
|
|
1709
|
+
color: var(--color-text, #222);
|
|
1710
|
+
border: 1px solid var(--color-border, #ccc);
|
|
1711
|
+
border-radius: 6px;
|
|
1712
|
+
box-shadow: 0 4px 16px rgba(0,0,0,0.18);
|
|
1713
|
+
padding: 4px 0;
|
|
1714
|
+
font: 13px system-ui, sans-serif;
|
|
1715
|
+
min-width: 180px;
|
|
1716
|
+
max-width: 320px;
|
|
1717
|
+
`;
|
|
1718
|
+
for (const it of items) {
|
|
1719
|
+
if (it.separator) {
|
|
1720
|
+
const sep = parentDoc.createElement("div");
|
|
1721
|
+
sep.style.cssText = "border-top:1px solid var(--color-border,#ddd); margin: 4px 0;";
|
|
1722
|
+
menu.appendChild(sep);
|
|
1723
|
+
continue;
|
|
1724
|
+
}
|
|
1725
|
+
const btn = parentDoc.createElement("button");
|
|
1726
|
+
btn.type = "button";
|
|
1727
|
+
btn.textContent = it.label;
|
|
1728
|
+
btn.style.cssText = `
|
|
1729
|
+
display: block; width: 100%; text-align: left;
|
|
1730
|
+
padding: 5px 12px; border: none; background: none;
|
|
1731
|
+
color: inherit; cursor: pointer; font: inherit;
|
|
1732
|
+
${it.emphasized ? "font-weight: 600;" : ""}
|
|
1733
|
+
`;
|
|
1734
|
+
btn.addEventListener("mouseenter", () => {
|
|
1735
|
+
btn.style.background = "var(--color-bg-hover, #eef)";
|
|
1736
|
+
});
|
|
1737
|
+
btn.addEventListener("mouseleave", () => {
|
|
1738
|
+
btn.style.background = "none";
|
|
1739
|
+
});
|
|
1740
|
+
btn.addEventListener("click", () => {
|
|
1741
|
+
try {
|
|
1742
|
+
it.action();
|
|
1743
|
+
} finally {
|
|
1744
|
+
menu.remove();
|
|
1850
1745
|
}
|
|
1851
|
-
|
|
1746
|
+
});
|
|
1747
|
+
menu.appendChild(btn);
|
|
1748
|
+
}
|
|
1749
|
+
parentDoc.body.appendChild(menu);
|
|
1750
|
+
const r = menu.getBoundingClientRect();
|
|
1751
|
+
if (r.right > window.innerWidth)
|
|
1752
|
+
menu.style.left = `${Math.max(8, window.innerWidth - r.width - 8)}px`;
|
|
1753
|
+
if (r.bottom > window.innerHeight)
|
|
1754
|
+
menu.style.top = `${Math.max(8, window.innerHeight - r.height - 8)}px`;
|
|
1755
|
+
const docs = [parentDoc, ...extraDismissDocs];
|
|
1756
|
+
const dismiss2 = (e) => {
|
|
1757
|
+
if (e.type === "keydown" && e.key !== "Escape")
|
|
1758
|
+
return;
|
|
1759
|
+
if (e.type === "mousedown" && menu.contains(e.target))
|
|
1760
|
+
return;
|
|
1761
|
+
menu.remove();
|
|
1762
|
+
for (const d of docs) {
|
|
1763
|
+
d.removeEventListener("mousedown", dismiss2, true);
|
|
1764
|
+
d.removeEventListener("keydown", dismiss2, true);
|
|
1852
1765
|
}
|
|
1766
|
+
};
|
|
1767
|
+
setTimeout(() => {
|
|
1768
|
+
for (const d of docs) {
|
|
1769
|
+
d.addEventListener("mousedown", dismiss2, true);
|
|
1770
|
+
d.addEventListener("keydown", dismiss2, true);
|
|
1771
|
+
}
|
|
1772
|
+
}, 0);
|
|
1773
|
+
}
|
|
1774
|
+
function getWordAtPoint(root, x, y) {
|
|
1775
|
+
const doc = root.ownerDocument || document;
|
|
1776
|
+
let node = null;
|
|
1777
|
+
let offset = 0;
|
|
1778
|
+
const winAny = doc.defaultView;
|
|
1779
|
+
if (typeof doc.caretPositionFromPoint === "function") {
|
|
1780
|
+
const pos = doc.caretPositionFromPoint(x, y);
|
|
1781
|
+
if (pos) {
|
|
1782
|
+
node = pos.offsetNode;
|
|
1783
|
+
offset = pos.offset;
|
|
1784
|
+
}
|
|
1785
|
+
} else if (typeof doc.caretRangeFromPoint === "function") {
|
|
1786
|
+
const range = doc.caretRangeFromPoint(x, y);
|
|
1787
|
+
if (range) {
|
|
1788
|
+
node = range.startContainer;
|
|
1789
|
+
offset = range.startOffset;
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
if (!node || node.nodeType !== Node.TEXT_NODE)
|
|
1793
|
+
return null;
|
|
1794
|
+
let walk = node;
|
|
1795
|
+
while (walk && walk !== root)
|
|
1796
|
+
walk = walk.parentNode;
|
|
1797
|
+
if (!walk)
|
|
1798
|
+
return null;
|
|
1799
|
+
let p = node.parentNode;
|
|
1800
|
+
while (p && p !== root) {
|
|
1801
|
+
if (p.nodeType === Node.ELEMENT_NODE && SKIP_TAGS.has(p.tagName))
|
|
1802
|
+
return null;
|
|
1803
|
+
p = p.parentNode;
|
|
1804
|
+
}
|
|
1805
|
+
const text = node.data;
|
|
1806
|
+
if (offset > text.length)
|
|
1807
|
+
offset = text.length;
|
|
1808
|
+
const isWordChar = (c) => /[\p{L}'’\-]/u.test(c);
|
|
1809
|
+
let start = offset;
|
|
1810
|
+
while (start > 0 && isWordChar(text[start - 1]))
|
|
1811
|
+
start--;
|
|
1812
|
+
let end = offset;
|
|
1813
|
+
while (end < text.length && isWordChar(text[end]))
|
|
1814
|
+
end++;
|
|
1815
|
+
if (end - start < MIN_WORD_LEN)
|
|
1816
|
+
return null;
|
|
1817
|
+
const word = text.slice(start, end);
|
|
1818
|
+
if (!/^[\p{L}]/u.test(word))
|
|
1819
|
+
return null;
|
|
1820
|
+
return { word, node, start, end };
|
|
1821
|
+
}
|
|
1822
|
+
var import_nspell, USER_DICT_KEY, MARKER_ATTR, MIN_WORD_LEN, SKIP_TAGS, spellPromise;
|
|
1823
|
+
var init_spellcheck_core = __esm({
|
|
1824
|
+
"client/compose/spellcheck-core.js"() {
|
|
1825
|
+
"use strict";
|
|
1826
|
+
import_nspell = __toESM(require_lib(), 1);
|
|
1827
|
+
init_api_client();
|
|
1828
|
+
USER_DICT_KEY = "mailx-user-dict";
|
|
1829
|
+
MARKER_ATTR = "data-mailx-spellerror";
|
|
1830
|
+
MIN_WORD_LEN = 3;
|
|
1831
|
+
SKIP_TAGS = /* @__PURE__ */ new Set(["BLOCKQUOTE", "CODE", "PRE", "A", "SCRIPT", "STYLE", "KBD", "SAMP", "VAR"]);
|
|
1832
|
+
spellPromise = null;
|
|
1853
1833
|
}
|
|
1854
1834
|
});
|
|
1855
1835
|
|
|
1856
|
-
//
|
|
1857
|
-
var
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
}
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1836
|
+
// client/lib/rmf-tiny.js
|
|
1837
|
+
var rmf_tiny_exports = {};
|
|
1838
|
+
__export(rmf_tiny_exports, {
|
|
1839
|
+
createTinyMceEditor: () => createTinyMceEditor
|
|
1840
|
+
});
|
|
1841
|
+
async function loadTinymce(opts) {
|
|
1842
|
+
try {
|
|
1843
|
+
const mod = await import(
|
|
1844
|
+
/* @vite-ignore */
|
|
1845
|
+
"tinymce"
|
|
1846
|
+
);
|
|
1847
|
+
const tinymce = mod.default || mod;
|
|
1848
|
+
await Promise.all([
|
|
1849
|
+
import("tinymce/themes/silver").catch(() => {
|
|
1850
|
+
}),
|
|
1851
|
+
import("tinymce/icons/default").catch(() => {
|
|
1852
|
+
}),
|
|
1853
|
+
import("tinymce/models/dom").catch(() => {
|
|
1854
|
+
}),
|
|
1855
|
+
import("tinymce/plugins/paste").catch(() => {
|
|
1856
|
+
}),
|
|
1857
|
+
import("tinymce/plugins/lists").catch(() => {
|
|
1858
|
+
}),
|
|
1859
|
+
import("tinymce/plugins/link").catch(() => {
|
|
1860
|
+
}),
|
|
1861
|
+
import("tinymce/plugins/table").catch(() => {
|
|
1862
|
+
}),
|
|
1863
|
+
import("tinymce/plugins/code").catch(() => {
|
|
1864
|
+
}),
|
|
1865
|
+
import("tinymce/plugins/image").catch(() => {
|
|
1866
|
+
})
|
|
1867
|
+
]);
|
|
1868
|
+
return tinymce;
|
|
1869
|
+
} catch {
|
|
1870
|
+
}
|
|
1871
|
+
const w = window;
|
|
1872
|
+
if (w.tinymce)
|
|
1873
|
+
return w.tinymce;
|
|
1874
|
+
if (!opts.cdnUrl) {
|
|
1875
|
+
throw new Error("rmf-tiny: tinymce not installed (npm install tinymce) and no cdnUrl supplied. See README for Android setup.");
|
|
1876
|
+
}
|
|
1877
|
+
const url = opts.apiKey && !opts.cdnUrl.includes("api-key") ? `${opts.cdnUrl}${opts.cdnUrl.includes("?") ? "&" : "?"}apiKey=${encodeURIComponent(opts.apiKey)}` : opts.cdnUrl;
|
|
1878
|
+
await new Promise((resolve, reject) => {
|
|
1879
|
+
const s = document.createElement("script");
|
|
1880
|
+
s.src = url;
|
|
1881
|
+
s.referrerPolicy = "origin";
|
|
1882
|
+
s.onload = () => resolve();
|
|
1883
|
+
s.onerror = () => reject(new Error(`rmf-tiny: failed to load TinyMCE from ${url}`));
|
|
1884
|
+
document.head.appendChild(s);
|
|
1885
|
+
});
|
|
1886
|
+
if (!w.tinymce)
|
|
1887
|
+
throw new Error("rmf-tiny: TinyMCE script loaded but window.tinymce is missing");
|
|
1888
|
+
return w.tinymce;
|
|
1889
|
+
}
|
|
1890
|
+
async function createTinyMceEditor(container2, opts = {}) {
|
|
1891
|
+
const tinymce = await loadTinymce(opts);
|
|
1892
|
+
const target = document.createElement("div");
|
|
1893
|
+
target.id = `rmf-tiny-${Math.random().toString(36).slice(2, 10)}`;
|
|
1894
|
+
target.style.cssText = "width:100%;height:100%;";
|
|
1895
|
+
container2.appendChild(target);
|
|
1896
|
+
const editor2 = await new Promise((resolve) => {
|
|
1897
|
+
tinymce.init({
|
|
1898
|
+
target,
|
|
1899
|
+
// Word-paste fidelity is the entire point of using TinyMCE here.
|
|
1900
|
+
// `paste_as_text: false` keeps formatting; powerpaste isn't in
|
|
1901
|
+
// OSS but the standard paste plugin handles Word reasonably well.
|
|
1902
|
+
// All free / OSS plugins bundled in the jsDelivr tinymce@6 script.
|
|
1903
|
+
// No premium plugins listed — listing one without an API key
|
|
1904
|
+
// triggers TinyMCE's upsell dialog on every load.
|
|
1905
|
+
plugins: "paste lists advlist link table code codesample image searchreplace autolink wordcount emoticons charmap insertdatetime quickbars nonbreaking directionality help",
|
|
1906
|
+
toolbar: [
|
|
1907
|
+
"undo redo | bold italic underline strikethrough | forecolor backcolor",
|
|
1908
|
+
"bullist numlist outdent indent | link table image code codesample | emoticons charmap | help"
|
|
1909
|
+
].join(" | "),
|
|
1910
|
+
// Include "tools" so wordcount and searchreplace are reachable.
|
|
1911
|
+
menubar: "file edit view insert format tools",
|
|
1912
|
+
// View menu — append zoom commands. fullscreen omitted: this editor
|
|
1913
|
+
// lives inside a compose iframe overlay that already fills its host
|
|
1914
|
+
// window; TinyMCE's fullscreen plugin re-positions the container in
|
|
1915
|
+
// ways that produce a blank body, so it's not in the plugin list.
|
|
1916
|
+
menu: {
|
|
1917
|
+
view: { title: "View", items: "code | visualaid visualchars visualblocks | preview | zoomIn zoomOut zoomReset" }
|
|
1918
|
+
},
|
|
1919
|
+
// Disable the "Get all features" upsell badge that TinyMCE 6+
|
|
1920
|
+
// injects automatically when no premium plugins are listed.
|
|
1921
|
+
promotion: false,
|
|
1922
|
+
// Floating toolbar that appears on text selection — keeps the
|
|
1923
|
+
// main toolbar uncluttered while exposing common formatters
|
|
1924
|
+
// where the cursor already is. quickbars_insert_toolbar:false
|
|
1925
|
+
// suppresses the second (insert-on-blank-line) popup which
|
|
1926
|
+
// clutters more than it helps in an email composer.
|
|
1927
|
+
quickbars_selection_toolbar: "bold italic underline | forecolor | quicklink blockquote",
|
|
1928
|
+
quickbars_insert_toolbar: false,
|
|
1929
|
+
quickbars_image_toolbar: "alignleft aligncenter alignright",
|
|
1930
|
+
// Code-sample dropdown languages. TinyMCE's default list omits
|
|
1931
|
+
// "Text" / plain — every option triggers syntax highlighting
|
|
1932
|
+
// which mangles unrelated paste content (Bob 2026-05-24).
|
|
1933
|
+
// Adding Text first so it's the default; rest are the modern
|
|
1934
|
+
// languages we actually paste.
|
|
1935
|
+
codesample_languages: [
|
|
1936
|
+
{ text: "Text", value: "text" },
|
|
1937
|
+
{ text: "HTML/XML", value: "markup" },
|
|
1938
|
+
{ text: "JavaScript", value: "javascript" },
|
|
1939
|
+
{ text: "TypeScript", value: "typescript" },
|
|
1940
|
+
{ text: "CSS", value: "css" },
|
|
1941
|
+
{ text: "JSON", value: "json" },
|
|
1942
|
+
{ text: "Python", value: "python" },
|
|
1943
|
+
{ text: "Java", value: "java" },
|
|
1944
|
+
{ text: "C", value: "c" },
|
|
1945
|
+
{ text: "C++", value: "cpp" },
|
|
1946
|
+
{ text: "C#", value: "csharp" },
|
|
1947
|
+
{ text: "Go", value: "go" },
|
|
1948
|
+
{ text: "Rust", value: "rust" },
|
|
1949
|
+
{ text: "Ruby", value: "ruby" },
|
|
1950
|
+
{ text: "PHP", value: "php" },
|
|
1951
|
+
{ text: "Shell", value: "bash" },
|
|
1952
|
+
{ text: "SQL", value: "sql" }
|
|
1953
|
+
],
|
|
1954
|
+
// WebView's native spell-check (red underlines, right-click
|
|
1955
|
+
// "Add to dictionary"). Free; same UX as Quill's spellcheck=true.
|
|
1956
|
+
// Premium tinymcespellchecker plugin would replace this with a
|
|
1957
|
+
// custom backend via spellchecker_rpc_url, but requires a
|
|
1958
|
+
// Tiny Cloud subscription.
|
|
1959
|
+
browser_spellcheck: true,
|
|
1960
|
+
// Right-click context menu DISABLED so WebView2's native menu
|
|
1961
|
+
// fires instead. We need the native menu because that's where
|
|
1962
|
+
// the browser shows spelling suggestions (TinyMCE's contextmenu
|
|
1963
|
+
// intercepts the right-click and replaces the menu — red
|
|
1964
|
+
// underlines appear but suggestions are unreachable). Trade-off:
|
|
1965
|
+
// lose TinyMCE's link / image / table quick actions. Standard
|
|
1966
|
+
// text formatting is still on the toolbar.
|
|
1967
|
+
contextmenu: false,
|
|
1968
|
+
statusbar: false,
|
|
1969
|
+
branding: false,
|
|
1970
|
+
license_key: "gpl",
|
|
1971
|
+
paste_data_images: true,
|
|
1972
|
+
// Permissive valid_elements — preserve as much of the source
|
|
1973
|
+
// formatting as possible. The default is more aggressive about
|
|
1974
|
+
// stripping. Empty string for `valid_elements` means accept
|
|
1975
|
+
// everything that the schema allows.
|
|
1976
|
+
paste_word_valid_elements: "@[style|class],-strong/b,-em/i,-u,-s,-sub,-sup,-strike,-p,-ol,-ul,-li,-h1,-h2,-h3,-h4,-h5,-h6,-blockquote,-table[border|cellpadding|cellspacing|width|height|class|style],-tr,-td[colspan|rowspan|width|height|class|style|valign|align|background|bgcolor],-th,-thead,-tbody,-tfoot,-pre,-br,-a[href|target|title],-img[src|alt|width|height|style|class]",
|
|
1977
|
+
paste_retain_style_properties: "color background background-color font-family font-size font-weight font-style text-decoration text-align padding padding-top padding-bottom padding-left padding-right margin margin-top margin-bottom margin-left margin-right border border-top border-bottom border-left border-right",
|
|
1978
|
+
// Auto-link bare URLs in pasted content. TinyMCE's `autolink`
|
|
1979
|
+
// plugin only fires on TYPED space/enter; URLs that arrive via
|
|
1980
|
+
// clipboard (browser address bar, terminal copy) come in as
|
|
1981
|
+
// plain text and stay un-linked. paste_preprocess runs on the
|
|
1982
|
+
// HTML the paste plugin produced.
|
|
1983
|
+
//
|
|
1984
|
+
// CRITICAL: skip auto-link when the content already contains
|
|
1985
|
+
// anchors. Naive regex over the whole content would wrap
|
|
1986
|
+
// `<a href="X">X</a>` in ANOTHER anchor (nested anchors are
|
|
1987
|
+
// invalid HTML and browsers split them, producing visible
|
|
1988
|
+
// junk). For HTML pastes that already have linked URLs (the
|
|
1989
|
+
// common case from a browser address bar or another mail
|
|
1990
|
+
// client), TinyMCE preserves them — no auto-link pass needed.
|
|
1991
|
+
// For plain-text pastes (no anchors present), wrap any bare
|
|
1992
|
+
// http(s)://… runs. Trailing sentence punctuation is excluded
|
|
1993
|
+
// from the URL.
|
|
1994
|
+
paste_preprocess: (_plugin, args) => {
|
|
1995
|
+
if (/<a[\s>]/i.test(args.content))
|
|
1996
|
+
return;
|
|
1997
|
+
args.content = args.content.replace(/(^|[\s(\[])((?:https?|ftp):\/\/[^\s<>"']+[^\s<>"'.,;:!?)\]])/gi, (_m, lead, url) => `${lead}<a href="${url}">${url}</a>`);
|
|
1998
|
+
},
|
|
1999
|
+
// Body font + quoted-reply styling. Without explicit rules for
|
|
2000
|
+
// <blockquote> and div.reply the editor iframe renders the
|
|
2001
|
+
// quoted block with no visual distinction from the user's own
|
|
2002
|
+
// text — pasted reply quotes vanish into a wall of unformatted
|
|
2003
|
+
// paragraphs (Bob 2026-05-12: "the body losing all formatting").
|
|
2004
|
+
// The styles below match Thunderbird/Outlook conventions: a
|
|
2005
|
+
// muted left-bar + indent on blockquotes, slight color shift on
|
|
2006
|
+
// the "On … wrote:" attribution, untouched typography for
|
|
2007
|
+
// everything else so genuine HTML formatting (bold / italic /
|
|
2008
|
+
// tables / inline color) still comes through verbatim.
|
|
2009
|
+
content_style: [
|
|
2010
|
+
// Dark-blue native caret bar. caret-shape:block produced a
|
|
2011
|
+
// column wider than the gap between letters AND caused the
|
|
2012
|
+
// caret to "scoot back to its old position" after an arrow
|
|
2013
|
+
// press (Chromium block-caret quirk with the arrow-key
|
|
2014
|
+
// selection adjustment, Bob 2026-05-24). Plain bar is
|
|
2015
|
+
// browser-default and behaves correctly with arrow keys;
|
|
2016
|
+
// just darken the color so it stays visible.
|
|
2017
|
+
"body { font-family: system-ui, sans-serif; font-size: 14px; caret-color: #0a2647; }",
|
|
2018
|
+
"blockquote { border-left: 3px solid #c0c8d0; margin: 0 0 0 4px; padding: 2px 0 2px 10px; color: #555; }",
|
|
2019
|
+
"div.reply { margin-top: 0.5em; }",
|
|
2020
|
+
"div.reply > p:first-child { color: #666; font-size: 0.95em; margin: 0 0 4px 0; }",
|
|
2021
|
+
"pre, code { font-family: ui-monospace, Consolas, Menlo, monospace; }"
|
|
2022
|
+
].join(" "),
|
|
2023
|
+
init_instance_callback: (ed) => resolve(ed),
|
|
2024
|
+
setup: (ed) => {
|
|
2025
|
+
if (opts.initialHtml)
|
|
2026
|
+
ed.on("init", () => ed.setContent(opts.initialHtml));
|
|
2027
|
+
const ZOOM_DEFAULT = 14;
|
|
2028
|
+
const ZOOM_STEP = 2;
|
|
2029
|
+
const ZOOM_MIN = 8;
|
|
2030
|
+
const ZOOM_MAX = 32;
|
|
2031
|
+
const ZOOM_STORAGE_KEY = "rmf-tiny:zoom-px";
|
|
2032
|
+
let zoomPx = ZOOM_DEFAULT;
|
|
2033
|
+
try {
|
|
2034
|
+
const stored = Number(localStorage.getItem(ZOOM_STORAGE_KEY));
|
|
2035
|
+
if (Number.isFinite(stored) && stored >= ZOOM_MIN && stored <= ZOOM_MAX) {
|
|
2036
|
+
zoomPx = stored;
|
|
1889
2037
|
}
|
|
1890
|
-
|
|
2038
|
+
} catch {
|
|
1891
2039
|
}
|
|
2040
|
+
const saveZoom = () => {
|
|
2041
|
+
try {
|
|
2042
|
+
localStorage.setItem(ZOOM_STORAGE_KEY, String(zoomPx));
|
|
2043
|
+
} catch {
|
|
2044
|
+
}
|
|
2045
|
+
};
|
|
2046
|
+
const applyZoom = () => {
|
|
2047
|
+
try {
|
|
2048
|
+
const body = ed.getBody();
|
|
2049
|
+
if (body)
|
|
2050
|
+
body.style.fontSize = `${zoomPx}px`;
|
|
2051
|
+
} catch {
|
|
2052
|
+
}
|
|
2053
|
+
};
|
|
2054
|
+
const bumpZoom = (delta) => {
|
|
2055
|
+
zoomPx = Math.max(ZOOM_MIN, Math.min(ZOOM_MAX, zoomPx + delta));
|
|
2056
|
+
applyZoom();
|
|
2057
|
+
saveZoom();
|
|
2058
|
+
};
|
|
2059
|
+
ed.ui.registry.addMenuItem("zoomIn", {
|
|
2060
|
+
text: "Zoom in",
|
|
2061
|
+
shortcut: "Ctrl+=",
|
|
2062
|
+
onAction: () => bumpZoom(ZOOM_STEP)
|
|
2063
|
+
});
|
|
2064
|
+
ed.ui.registry.addMenuItem("zoomOut", {
|
|
2065
|
+
text: "Zoom out",
|
|
2066
|
+
shortcut: "Ctrl+-",
|
|
2067
|
+
onAction: () => bumpZoom(-ZOOM_STEP)
|
|
2068
|
+
});
|
|
2069
|
+
ed.ui.registry.addMenuItem("zoomReset", {
|
|
2070
|
+
text: "Reset zoom",
|
|
2071
|
+
shortcut: "Ctrl+0",
|
|
2072
|
+
onAction: () => {
|
|
2073
|
+
zoomPx = ZOOM_DEFAULT;
|
|
2074
|
+
applyZoom();
|
|
2075
|
+
saveZoom();
|
|
2076
|
+
}
|
|
2077
|
+
});
|
|
2078
|
+
const installLinkEscape = () => {
|
|
2079
|
+
const doc = ed.getDoc();
|
|
2080
|
+
if (!doc || doc.__rmfLinkEscapeInstalled)
|
|
2081
|
+
return;
|
|
2082
|
+
doc.__rmfLinkEscapeInstalled = true;
|
|
2083
|
+
doc.addEventListener("beforeinput", (e) => {
|
|
2084
|
+
const kind = e.inputType || "";
|
|
2085
|
+
const isInsert = kind === "insertText" || kind === "insertCompositionText" || kind === "insertFromPaste" || kind === "insertFromDrop" || kind === "insertFromComposition";
|
|
2086
|
+
if (!isInsert)
|
|
2087
|
+
return;
|
|
2088
|
+
const sel = doc.getSelection();
|
|
2089
|
+
if (!sel || sel.rangeCount === 0)
|
|
2090
|
+
return;
|
|
2091
|
+
const rng = sel.getRangeAt(0);
|
|
2092
|
+
if (!rng.collapsed)
|
|
2093
|
+
return;
|
|
2094
|
+
const node = rng.startContainer;
|
|
2095
|
+
const a = node.nodeType === Node.ELEMENT_NODE ? node : node.parentNode;
|
|
2096
|
+
if (!a)
|
|
2097
|
+
return;
|
|
2098
|
+
const link = a.closest?.("a");
|
|
2099
|
+
if (!link)
|
|
2100
|
+
return;
|
|
2101
|
+
let tail;
|
|
2102
|
+
try {
|
|
2103
|
+
tail = rng.cloneRange();
|
|
2104
|
+
tail.setEndAfter(link);
|
|
2105
|
+
} catch {
|
|
2106
|
+
return;
|
|
2107
|
+
}
|
|
2108
|
+
if (tail.toString().length > 0)
|
|
2109
|
+
return;
|
|
2110
|
+
const after = doc.createRange();
|
|
2111
|
+
after.setStartAfter(link);
|
|
2112
|
+
after.collapse(true);
|
|
2113
|
+
sel.removeAllRanges();
|
|
2114
|
+
sel.addRange(after);
|
|
2115
|
+
}, true);
|
|
2116
|
+
};
|
|
2117
|
+
ed.on("init", installLinkEscape);
|
|
2118
|
+
ed.on("SetContent", installLinkEscape);
|
|
2119
|
+
ed.on("init", () => {
|
|
2120
|
+
try {
|
|
2121
|
+
const body = ed.getBody();
|
|
2122
|
+
if (body) {
|
|
2123
|
+
body.setAttribute("spellcheck", "true");
|
|
2124
|
+
body.setAttribute("lang", "en");
|
|
2125
|
+
}
|
|
2126
|
+
const doc = ed.getDoc();
|
|
2127
|
+
if (doc?.documentElement)
|
|
2128
|
+
doc.documentElement.setAttribute("lang", "en");
|
|
2129
|
+
} catch {
|
|
2130
|
+
}
|
|
2131
|
+
if (zoomPx !== ZOOM_DEFAULT)
|
|
2132
|
+
applyZoom();
|
|
2133
|
+
try {
|
|
2134
|
+
const doc = ed.getDoc();
|
|
2135
|
+
doc.addEventListener("wheel", (e) => {
|
|
2136
|
+
if (!e.ctrlKey)
|
|
2137
|
+
return;
|
|
2138
|
+
e.preventDefault();
|
|
2139
|
+
bumpZoom(e.deltaY < 0 ? ZOOM_STEP : -ZOOM_STEP);
|
|
2140
|
+
}, { passive: false });
|
|
2141
|
+
doc.addEventListener("keydown", (e) => {
|
|
2142
|
+
if (!(e.ctrlKey || e.metaKey))
|
|
2143
|
+
return;
|
|
2144
|
+
if (e.key === "=" || e.key === "+") {
|
|
2145
|
+
e.preventDefault();
|
|
2146
|
+
e.stopPropagation();
|
|
2147
|
+
bumpZoom(ZOOM_STEP);
|
|
2148
|
+
} else if (e.key === "-") {
|
|
2149
|
+
e.preventDefault();
|
|
2150
|
+
e.stopPropagation();
|
|
2151
|
+
bumpZoom(-ZOOM_STEP);
|
|
2152
|
+
} else if (e.key === "0") {
|
|
2153
|
+
e.preventDefault();
|
|
2154
|
+
e.stopPropagation();
|
|
2155
|
+
zoomPx = ZOOM_DEFAULT;
|
|
2156
|
+
applyZoom();
|
|
2157
|
+
}
|
|
2158
|
+
}, true);
|
|
2159
|
+
} catch {
|
|
2160
|
+
}
|
|
2161
|
+
});
|
|
1892
2162
|
}
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
2163
|
+
});
|
|
2164
|
+
});
|
|
2165
|
+
return {
|
|
2166
|
+
setHtml(html) {
|
|
2167
|
+
editor2.setContent(html);
|
|
2168
|
+
},
|
|
2169
|
+
getHtml() {
|
|
2170
|
+
return editor2.getContent();
|
|
2171
|
+
},
|
|
2172
|
+
getText() {
|
|
2173
|
+
return editor2.getContent({ format: "text" });
|
|
2174
|
+
},
|
|
2175
|
+
focus() {
|
|
2176
|
+
editor2.focus();
|
|
2177
|
+
},
|
|
2178
|
+
setCursor(pos) {
|
|
2179
|
+
const place = () => {
|
|
2180
|
+
const body = editor2.getBody();
|
|
2181
|
+
if (pos === 0) {
|
|
2182
|
+
const first = body.firstChild;
|
|
2183
|
+
if (first && first.nodeType === 1) {
|
|
2184
|
+
editor2.selection.setCursorLocation(first, 0);
|
|
2185
|
+
} else {
|
|
2186
|
+
editor2.selection.select(body, true);
|
|
2187
|
+
editor2.selection.collapse(true);
|
|
1908
2188
|
}
|
|
2189
|
+
editor2.focus();
|
|
2190
|
+
editor2.getWin()?.scrollTo(0, 0);
|
|
2191
|
+
} else {
|
|
2192
|
+
editor2.selection.select(body, true);
|
|
2193
|
+
editor2.selection.collapse(false);
|
|
2194
|
+
editor2.focus();
|
|
2195
|
+
editor2.selection.scrollIntoView();
|
|
1909
2196
|
}
|
|
2197
|
+
};
|
|
2198
|
+
try {
|
|
2199
|
+
place();
|
|
2200
|
+
editor2.getWin()?.requestAnimationFrame?.(() => {
|
|
2201
|
+
try {
|
|
2202
|
+
place();
|
|
2203
|
+
} catch {
|
|
2204
|
+
}
|
|
2205
|
+
});
|
|
2206
|
+
} catch {
|
|
1910
2207
|
}
|
|
1911
|
-
}
|
|
2208
|
+
},
|
|
2209
|
+
get root() {
|
|
2210
|
+
return editor2.getContainer();
|
|
2211
|
+
},
|
|
2212
|
+
on(event, handler) {
|
|
2213
|
+
editor2.on(event, handler);
|
|
2214
|
+
},
|
|
2215
|
+
off(event, handler) {
|
|
2216
|
+
editor2.off(event, handler);
|
|
2217
|
+
},
|
|
2218
|
+
nativeEditor: editor2
|
|
2219
|
+
};
|
|
2220
|
+
}
|
|
2221
|
+
var init_rmf_tiny = __esm({
|
|
2222
|
+
"client/lib/rmf-tiny.js"() {
|
|
2223
|
+
"use strict";
|
|
1912
2224
|
}
|
|
1913
2225
|
});
|
|
1914
2226
|
|
|
@@ -1917,10 +2229,10 @@ var spellcheck_exports = {};
|
|
|
1917
2229
|
__export(spellcheck_exports, {
|
|
1918
2230
|
wireSpellcheck: () => wireSpellcheck
|
|
1919
2231
|
});
|
|
1920
|
-
async function
|
|
1921
|
-
if (
|
|
1922
|
-
return
|
|
1923
|
-
|
|
2232
|
+
async function getSpell2() {
|
|
2233
|
+
if (spellPromise2)
|
|
2234
|
+
return spellPromise2;
|
|
2235
|
+
spellPromise2 = (async () => {
|
|
1924
2236
|
const [affRes, dicRes] = await Promise.all([
|
|
1925
2237
|
fetch("../lib/dict/en.aff"),
|
|
1926
2238
|
fetch("../lib/dict/en.dic")
|
|
@@ -1929,9 +2241,9 @@ async function getSpell() {
|
|
|
1929
2241
|
throw new Error(`spellcheck: dict fetch failed (aff=${affRes.status} dic=${dicRes.status})`);
|
|
1930
2242
|
}
|
|
1931
2243
|
const [aff, dic] = await Promise.all([affRes.text(), dicRes.text()]);
|
|
1932
|
-
const sp = new
|
|
2244
|
+
const sp = new import_nspell2.default({ aff, dic });
|
|
1933
2245
|
try {
|
|
1934
|
-
const raw = localStorage.getItem(
|
|
2246
|
+
const raw = localStorage.getItem(USER_DICT_KEY2);
|
|
1935
2247
|
if (raw)
|
|
1936
2248
|
for (const w of JSON.parse(raw))
|
|
1937
2249
|
sp.add(w);
|
|
@@ -1943,7 +2255,7 @@ async function getSpell() {
|
|
|
1943
2255
|
sp.add(w);
|
|
1944
2256
|
let local = [];
|
|
1945
2257
|
try {
|
|
1946
|
-
const raw = localStorage.getItem(
|
|
2258
|
+
const raw = localStorage.getItem(USER_DICT_KEY2);
|
|
1947
2259
|
local = raw ? JSON.parse(raw) : [];
|
|
1948
2260
|
} catch {
|
|
1949
2261
|
local = [];
|
|
@@ -1955,22 +2267,22 @@ async function getSpell() {
|
|
|
1955
2267
|
}
|
|
1956
2268
|
try {
|
|
1957
2269
|
const merged = [.../* @__PURE__ */ new Set([...local, ...cloudArr])];
|
|
1958
|
-
localStorage.setItem(
|
|
2270
|
+
localStorage.setItem(USER_DICT_KEY2, JSON.stringify(merged));
|
|
1959
2271
|
} catch {
|
|
1960
2272
|
}
|
|
1961
2273
|
}).catch(() => {
|
|
1962
2274
|
});
|
|
1963
2275
|
return sp;
|
|
1964
2276
|
})();
|
|
1965
|
-
return
|
|
2277
|
+
return spellPromise2;
|
|
1966
2278
|
}
|
|
1967
|
-
function
|
|
2279
|
+
function addToUserDict2(word, sp) {
|
|
1968
2280
|
try {
|
|
1969
|
-
const raw = localStorage.getItem(
|
|
2281
|
+
const raw = localStorage.getItem(USER_DICT_KEY2);
|
|
1970
2282
|
const arr = raw ? JSON.parse(raw) : [];
|
|
1971
2283
|
if (!arr.includes(word)) {
|
|
1972
2284
|
arr.push(word);
|
|
1973
|
-
localStorage.setItem(
|
|
2285
|
+
localStorage.setItem(USER_DICT_KEY2, JSON.stringify(arr));
|
|
1974
2286
|
}
|
|
1975
2287
|
} catch {
|
|
1976
2288
|
}
|
|
@@ -1998,7 +2310,7 @@ function decorate(editor2, sp) {
|
|
|
1998
2310
|
const savedBodyScrollTop = body.scrollTop;
|
|
1999
2311
|
try {
|
|
2000
2312
|
editor2.undoManager?.ignore?.(() => {
|
|
2001
|
-
const old = body.querySelectorAll(`span[${
|
|
2313
|
+
const old = body.querySelectorAll(`span[${MARKER_ATTR2}]`);
|
|
2002
2314
|
for (const m of old) {
|
|
2003
2315
|
const parent2 = m.parentNode;
|
|
2004
2316
|
if (!parent2)
|
|
@@ -2012,7 +2324,7 @@ function decorate(editor2, sp) {
|
|
|
2012
2324
|
acceptNode(node) {
|
|
2013
2325
|
let p = node.parentNode;
|
|
2014
2326
|
while (p && p !== body) {
|
|
2015
|
-
if (p.nodeType === Node.ELEMENT_NODE &&
|
|
2327
|
+
if (p.nodeType === Node.ELEMENT_NODE && SKIP_TAGS2.has(p.tagName)) {
|
|
2016
2328
|
return NodeFilter.FILTER_REJECT;
|
|
2017
2329
|
}
|
|
2018
2330
|
p = p.parentNode;
|
|
@@ -2047,7 +2359,7 @@ function decorate(editor2, sp) {
|
|
|
2047
2359
|
WORD_RE.lastIndex = 0;
|
|
2048
2360
|
while ((m = WORD_RE.exec(text)) !== null) {
|
|
2049
2361
|
const word = m[0];
|
|
2050
|
-
if (word.length <
|
|
2362
|
+
if (word.length < MIN_WORD_LEN2)
|
|
2051
2363
|
continue;
|
|
2052
2364
|
const wStart = m.index, wEnd = m.index + word.length;
|
|
2053
2365
|
if (emailRanges.some(([s, e]) => wStart < e && wEnd > s))
|
|
@@ -2067,7 +2379,7 @@ function decorate(editor2, sp) {
|
|
|
2067
2379
|
range.setStart(h.node, h.start);
|
|
2068
2380
|
range.setEnd(h.node, h.end);
|
|
2069
2381
|
const span = doc.createElement("span");
|
|
2070
|
-
span.setAttribute(
|
|
2382
|
+
span.setAttribute(MARKER_ATTR2, "1");
|
|
2071
2383
|
try {
|
|
2072
2384
|
range.surroundContents(span);
|
|
2073
2385
|
} catch {
|
|
@@ -2172,7 +2484,7 @@ function installDecorationStyle(editor2) {
|
|
|
2172
2484
|
const style = doc.createElement("style");
|
|
2173
2485
|
style.id = "mailx-spell-style";
|
|
2174
2486
|
style.textContent = `
|
|
2175
|
-
span[${
|
|
2487
|
+
span[${MARKER_ATTR2}] {
|
|
2176
2488
|
text-decoration: underline wavy #d33;
|
|
2177
2489
|
text-decoration-skip-ink: none;
|
|
2178
2490
|
text-underline-offset: 2px;
|
|
@@ -2188,7 +2500,7 @@ function installSerializerFilter(editor2) {
|
|
|
2188
2500
|
return;
|
|
2189
2501
|
editor2.__mailxSpellSerializerWired = true;
|
|
2190
2502
|
try {
|
|
2191
|
-
editor2.serializer.addAttributeFilter(
|
|
2503
|
+
editor2.serializer.addAttributeFilter(MARKER_ATTR2, (nodes) => {
|
|
2192
2504
|
for (const node of nodes) {
|
|
2193
2505
|
if (typeof node.unwrap === "function")
|
|
2194
2506
|
node.unwrap();
|
|
@@ -2198,7 +2510,7 @@ function installSerializerFilter(editor2) {
|
|
|
2198
2510
|
console.warn("[spellcheck] serializer filter setup failed:", e);
|
|
2199
2511
|
}
|
|
2200
2512
|
}
|
|
2201
|
-
function
|
|
2513
|
+
function showSuggestionsMenu2(parentDoc, x, y, items) {
|
|
2202
2514
|
parentDoc.getElementById("mailx-spell-menu")?.remove();
|
|
2203
2515
|
const menu = parentDoc.createElement("div");
|
|
2204
2516
|
menu.id = "mailx-spell-menu";
|
|
@@ -2313,7 +2625,7 @@ function cleanupCorrected(editor2, sp) {
|
|
|
2313
2625
|
const activeSel = doc.getSelection();
|
|
2314
2626
|
if (activeSel && activeSel.rangeCount > 0 && !activeSel.isCollapsed)
|
|
2315
2627
|
return;
|
|
2316
|
-
const markers = body.querySelectorAll(`span[${
|
|
2628
|
+
const markers = body.querySelectorAll(`span[${MARKER_ATTR2}]`);
|
|
2317
2629
|
if (markers.length === 0)
|
|
2318
2630
|
return;
|
|
2319
2631
|
let caretMarker = null;
|
|
@@ -2321,7 +2633,7 @@ function cleanupCorrected(editor2, sp) {
|
|
|
2321
2633
|
if (sel && sel.rangeCount > 0) {
|
|
2322
2634
|
let p = sel.focusNode;
|
|
2323
2635
|
while (p && p !== body) {
|
|
2324
|
-
if (p.nodeType === Node.ELEMENT_NODE && p.hasAttribute?.(
|
|
2636
|
+
if (p.nodeType === Node.ELEMENT_NODE && p.hasAttribute?.(MARKER_ATTR2)) {
|
|
2325
2637
|
caretMarker = p;
|
|
2326
2638
|
break;
|
|
2327
2639
|
}
|
|
@@ -2395,7 +2707,7 @@ function wireSpellcheck(editor2) {
|
|
|
2395
2707
|
cleanupCorrected(editor2, sp);
|
|
2396
2708
|
}, CLEANUP_DEBOUNCE_MS);
|
|
2397
2709
|
};
|
|
2398
|
-
|
|
2710
|
+
getSpell2().then((loaded) => {
|
|
2399
2711
|
sp = loaded;
|
|
2400
2712
|
installDecorationStyle(editor2);
|
|
2401
2713
|
installSerializerFilter(editor2);
|
|
@@ -2411,7 +2723,7 @@ function wireSpellcheck(editor2) {
|
|
|
2411
2723
|
const target = e.target;
|
|
2412
2724
|
if (!target)
|
|
2413
2725
|
return;
|
|
2414
|
-
const marker = target.closest?.(`span[${
|
|
2726
|
+
const marker = target.closest?.(`span[${MARKER_ATTR2}]`);
|
|
2415
2727
|
if (!marker)
|
|
2416
2728
|
return;
|
|
2417
2729
|
const word = marker.textContent || "";
|
|
@@ -2458,7 +2770,7 @@ function wireSpellcheck(editor2) {
|
|
|
2458
2770
|
label: `Add "${word}" to dictionary`,
|
|
2459
2771
|
action: () => {
|
|
2460
2772
|
if (sp)
|
|
2461
|
-
|
|
2773
|
+
addToUserDict2(word, sp);
|
|
2462
2774
|
scheduleDecorate();
|
|
2463
2775
|
}
|
|
2464
2776
|
});
|
|
@@ -2470,22 +2782,22 @@ function wireSpellcheck(editor2) {
|
|
|
2470
2782
|
scheduleDecorate();
|
|
2471
2783
|
}
|
|
2472
2784
|
});
|
|
2473
|
-
|
|
2785
|
+
showSuggestionsMenu2(document, iframeRect.left + e.clientX, iframeRect.top + e.clientY, items);
|
|
2474
2786
|
}, true);
|
|
2475
2787
|
}
|
|
2476
|
-
var
|
|
2788
|
+
var import_nspell2, USER_DICT_KEY2, MARKER_ATTR2, DECORATE_DEBOUNCE_MS, CLEANUP_DEBOUNCE_MS, MIN_WORD_LEN2, SKIP_TAGS2, spellPromise2;
|
|
2477
2789
|
var init_spellcheck = __esm({
|
|
2478
2790
|
"client/compose/spellcheck.js"() {
|
|
2479
2791
|
"use strict";
|
|
2480
|
-
|
|
2792
|
+
import_nspell2 = __toESM(require_lib(), 1);
|
|
2481
2793
|
init_api_client();
|
|
2482
|
-
|
|
2483
|
-
|
|
2794
|
+
USER_DICT_KEY2 = "mailx-user-dict";
|
|
2795
|
+
MARKER_ATTR2 = "data-mailx-spellerror";
|
|
2484
2796
|
DECORATE_DEBOUNCE_MS = 1200;
|
|
2485
2797
|
CLEANUP_DEBOUNCE_MS = 300;
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2798
|
+
MIN_WORD_LEN2 = 3;
|
|
2799
|
+
SKIP_TAGS2 = /* @__PURE__ */ new Set(["BLOCKQUOTE", "CODE", "PRE", "A", "SCRIPT", "STYLE", "KBD", "SAMP", "VAR"]);
|
|
2800
|
+
spellPromise2 = null;
|
|
2489
2801
|
}
|
|
2490
2802
|
});
|
|
2491
2803
|
|
|
@@ -2952,6 +3264,72 @@ function createQuillEditor(container2) {
|
|
|
2952
3264
|
toolbar?.addHandler("link", function() {
|
|
2953
3265
|
openLinkForRange(q, q.getSelection() || { index: q.getLength() - 1, length: 0 });
|
|
2954
3266
|
});
|
|
3267
|
+
let _spellSp = null;
|
|
3268
|
+
Promise.resolve().then(() => (init_spellcheck_core(), spellcheck_core_exports)).then((m) => m.getSpell()).then((sp) => {
|
|
3269
|
+
_spellSp = sp;
|
|
3270
|
+
}).catch(() => {
|
|
3271
|
+
});
|
|
3272
|
+
q.root.addEventListener("contextmenu", async (e) => {
|
|
3273
|
+
try {
|
|
3274
|
+
if (!_spellSp)
|
|
3275
|
+
return;
|
|
3276
|
+
const sel = q.getSelection();
|
|
3277
|
+
if (sel && sel.length > 0)
|
|
3278
|
+
return;
|
|
3279
|
+
const core = await Promise.resolve().then(() => (init_spellcheck_core(), spellcheck_core_exports));
|
|
3280
|
+
const hit = core.getWordAtPoint(q.root, e.clientX, e.clientY);
|
|
3281
|
+
if (!hit)
|
|
3282
|
+
return;
|
|
3283
|
+
const { word, node, start, end } = hit;
|
|
3284
|
+
if (_spellSp.correct(word))
|
|
3285
|
+
return;
|
|
3286
|
+
e.preventDefault();
|
|
3287
|
+
e.stopPropagation();
|
|
3288
|
+
const sugs = core.buildSuggestionList(word, _spellSp);
|
|
3289
|
+
const items = [];
|
|
3290
|
+
if (sugs.length === 0) {
|
|
3291
|
+
items.push({ label: "(no suggestions)", action: () => {
|
|
3292
|
+
} });
|
|
3293
|
+
} else {
|
|
3294
|
+
for (const s of sugs) {
|
|
3295
|
+
items.push({
|
|
3296
|
+
label: s,
|
|
3297
|
+
emphasized: true,
|
|
3298
|
+
action: () => {
|
|
3299
|
+
try {
|
|
3300
|
+
const range = document.createRange();
|
|
3301
|
+
range.setStart(node, start);
|
|
3302
|
+
range.setEnd(node, end);
|
|
3303
|
+
const docSel = document.getSelection();
|
|
3304
|
+
if (docSel) {
|
|
3305
|
+
docSel.removeAllRanges();
|
|
3306
|
+
docSel.addRange(range);
|
|
3307
|
+
if (!document.execCommand("insertText", false, s)) {
|
|
3308
|
+
range.deleteContents();
|
|
3309
|
+
range.insertNode(document.createTextNode(s));
|
|
3310
|
+
}
|
|
3311
|
+
}
|
|
3312
|
+
} catch {
|
|
3313
|
+
}
|
|
3314
|
+
}
|
|
3315
|
+
});
|
|
3316
|
+
}
|
|
3317
|
+
}
|
|
3318
|
+
items.push({ label: "", action: () => {
|
|
3319
|
+
}, separator: true });
|
|
3320
|
+
items.push({
|
|
3321
|
+
label: `Add "${word}" to dictionary`,
|
|
3322
|
+
action: () => core.addToUserDict(word, _spellSp)
|
|
3323
|
+
});
|
|
3324
|
+
items.push({
|
|
3325
|
+
label: "Ignore (this session)",
|
|
3326
|
+
action: () => _spellSp.add(word)
|
|
3327
|
+
});
|
|
3328
|
+
core.showSuggestionsMenu(document, e.clientX, e.clientY, items);
|
|
3329
|
+
} catch (err) {
|
|
3330
|
+
console.warn("[spellcheck] right-click handler error:", err?.message || err);
|
|
3331
|
+
}
|
|
3332
|
+
});
|
|
2955
3333
|
q.root.addEventListener("contextmenu", async (e) => {
|
|
2956
3334
|
try {
|
|
2957
3335
|
const sel = q.getSelection();
|
|
@@ -3409,6 +3787,7 @@ window.addEventListener("message", (e) => {
|
|
|
3409
3787
|
});
|
|
3410
3788
|
|
|
3411
3789
|
// client/compose/compose.ts
|
|
3790
|
+
installConsoleCapture();
|
|
3412
3791
|
logClientEvent("compose-module-loaded", { href: location.href, version: window.mailxVersion || "?" });
|
|
3413
3792
|
var _composeT0 = performance.now();
|
|
3414
3793
|
function _ctick(label) {
|