-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbackground.js
185 lines (160 loc) · 6.29 KB
/
background.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
import { DEFAULT_PREFS } from "./prefs.js";
import {
// procFieldDataFuncs,
procHeaderFieldData,
fieldNameStyleBold,
fieldNameStyleNone,
createDateParts,
getDateFormatString,
createPrefsObj,
isEmptyObj
} from "./extfuncs.js";
let prefs = {};
async function setPrefs() {
prefs = await messenger.storage.local.get()
.catch(_ => DEFAULT_PREFS)
.then(v => createPrefsObj(Object.keys(v).length && v || DEFAULT_PREFS));
}
// Load preferences on app startup
messenger.runtime.onStartup.addListener(setPrefs);
(async () => {
const parseTextToHtml = text => new DOMParser().parseFromString(text,"text/html");
const htmlToText = node => new XMLSerializer().serializeToString(node);
function transformHTMLBody(body,type,newHeader) {
const html = parseTextToHtml(body);
const headerNode = type === "reply" ? "moz-cite-prefix" : "moz-email-headers-table";
const oldHeader = html.getElementsByClassName(headerNode)[0];
const wrapper = oldHeader.parentNode;
wrapper.insertBefore(newHeader,oldHeader);
oldHeader.remove();
if (type !== "reply") { newHeader.previousSibling.remove(); }
if (!prefs.removeSeparatorLine) { wrapper.insertBefore(createSeparatorLine(prefs),newHeader); }
return htmlToText(html);
}
function transformPlaintextBody(body,type,newHeader) {
if (type === "reply") {
let endIdx = body.indexOf("wrote:\n>");
return `\n\n-------- Original Message --------\n${newHeader}\n\n${body.slice(endIdx+6)}`;
} else {
const fwdHeaderMatch = body.match(/\nTo:\s+[^\n]+\n/);
if (!fwdHeaderMatch) { console.error("transformPlaintextBody: error with regex match"); }
let startBodyIndex = fwdHeaderMatch.index + fwdHeaderMatch[0].length;
let contentBody = body.slice(startBodyIndex);
contentBody = contentBody.slice(contentBody.indexOf("\n\n"));
return `\n\n-------- Forwarded Message --------\n${newHeader}\n${contentBody}`;
}
}
function createSeparatorLine(prefObj) {
const {
lineSeparatorStyle,
lineSeparatorWidth,
lineSeparatorColor
} = prefObj;
const hr = document.createElement("hr");
hr.style.borderStyle = "none";
hr.style.borderTopStyle = lineSeparatorStyle;
hr.style.borderTopWidth = `${lineSeparatorWidth}px`;
hr.style.borderTopColor = lineSeparatorColor;
hr.style.paddingLeft = "1.5em";
hr.style.paddingRight = "1.5em";
return hr;
}
function parseEmailAddressString(str) {
const regx = /\s<[^>]+@.+\.\w+>$/;
if (!regx.test(str)) { return ["",str]; }
const match = str.match(regx);
const address = match[0].slice(2,-1);
const name = str.slice(0,match.index);
return [ name,address ];
}
function refineFields(fields) {
const refFields = Object.assign({},fields);
refFields.author = parseEmailAddressString(refFields.author);
refFields.recipients = refFields.recipients.map(parseEmailAddressString);
refFields.ccList = refFields.ccList.map(parseEmailAddressString);
return refFields;
}
function fontStyling() {
const fontFamily = ["monospace","serif","sans-serif","cursive","system-ui",prefs.fontFaceCustom][prefs.fontFace];
const colorStr = /^#0+$/.test(prefs.fontColor) ? "" : `color: ${prefs.fontColor}; `;// allows visibility of text in TB dark theme
return `font-family: ${fontFamily}; ${colorStr}font-size: ${prefs.fontSize}px;`;
}
async function makeEnhancedHeaderTable(fields) {
const headerFieldData = refineFields(fields);
const table = document.createElement("table"),
tbody = table.appendChild(document.createElement("tbody")),
fieldDataList = procHeaderFieldData(prefs,headerFieldData,new Set(prefs.fieldOrder),prefs.dateTimeOptions),
fontStyle = fontStyling();
const padpix = 5;/// tentative default
["border-spacing","cellpadding","border"].forEach(prop => table.setAttribute(prop,0));
table.setAttribute("class","ehr-email-headers-table");
fieldDataList.forEach(([name,data]) => {
if (!data.length) { return; }
const tr = tbody.appendChild(document.createElement("tr")),
th = tr.appendChild(document.createElement("th")),
td = tr.appendChild(document.createElement("td")),
fn = prefs.boldFieldNames ? fieldNameStyleBold : fieldNameStyleNone,
[ prop,val ] = fn(name,data);
[
["vertical-align","baseline"],
["nowrap","nowrap"],
["style",`text-align:left; padding-right:${padpix}px; ${fontStyle}`]
].forEach(([k,v]) => th.setAttribute(k,v));
td.setAttribute("style",fontStyle);
th.appendChild(prop);
td.appendChild(val);
});
return table;
}
async function composeTransform(details) {
let {
body,
isPlainText,
plainTextBody,
relatedMessageId,
type
} = details;
const {
author,
ccList,// array
date,
recipients,// array
subject
} = await messenger.messages.get(relatedMessageId);
const { headers } = await messenger.messages.getFull(relatedMessageId);
const replyTo = headers["reply-to"] || [];
const enhancedHeader = await makeEnhancedHeaderTable({
author,
ccList,
date,
recipients,
subject,
replyTo
});
if (!isPlainText) { body = await transformHTMLBody(body,type,enhancedHeader); }
// always modify plaintext header
plainTextBody = transformPlaintextBody(plainTextBody,type,nodeHeaderToText(enhancedHeader));
return { plainTextBody, body };
}
function nodeHeaderToText(node) {
const fields = Array.from(node.querySelector("tbody").children);
return fields.map(child => Array.from(child.children).map(el => el.textContent).join("")).join("\n");
}
// Receive update from options page
await messenger.runtime.onMessage.addListener(async obj => {
prefs = createPrefsObj(obj);
await messenger.storage.local.set(createPrefsObj(prefs));
});
// Mutate reply/forward messages with updated header details
await messenger.tabs.onCreated.addListener(async tab => {
// console.debug(`windowId: ${tab.windowId}\ntabId: ${tab.id}`);
if (isEmptyObj(prefs)) { setPrefs(); }
if (tab.type === "messageCompose") {
const composeDetails = await messenger.compose.getComposeDetails(tab.id);
if (["reply","forward"].includes(composeDetails.type)) {
// console.log(composeDetails);
await messenger.compose.setComposeDetails(tab.id,await composeTransform(composeDetails));
}
}
});
})();