-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcontent.js
151 lines (128 loc) · 5.14 KB
/
content.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
const PRICE_REGEX = /(?:(?<=\s)|^)(?:(?:[£€$¥₹₽]|Rs\.?)\s*\d{1,3}(?:[,.\s]\d{3})*(?:[.,]\d{0,2})?|\d{1,3}(?:[,.\s]\d{3})*(?:[.,]\d{0,2})?\s*(?:[£€$¥₹₽]|Rs\.?))(?:(?=\s)|$)/g;
const CONVERSION_RATES_TO_EUR = {
'£': 0.85, '€': 1, '$': 0.90, '¥': 0.008, '₹': 0.011, '₽': 0.011, 'Rs': 0.011
};
const DAYS_IN_YEAR = 365.25;
const MONTHS_IN_YEAR = 12.0;
const DAYS_IN_WEEK = 7.0;
const MINUTES_IN_HOUR = 60.0;
let hourWorth = null;
async function refreshHourWorth() {
hourWorth = await getHourWorthFromStorage();
}
async function getHourWorthFromStorage() {
return new Promise((resolve, reject) => {
browser.storage.local.get('settings', data => {
if (data.settings) {
resolve(computeHourWorth(data.settings));
} else {
reject("No settings found.");
}
});
});
}
function computeHourWorth(settings) {
const {
monthlyIncome, weeklyWorkdays, dailyWorkHours,
dailyCommuteMinutes, monthlyCommuteCost, vacationDays
} = settings;
const monthlyWorkHours = computeMonthlyWorkHours(weeklyWorkdays, dailyWorkHours, dailyCommuteMinutes, vacationDays);
return (monthlyIncome - monthlyCommuteCost) / monthlyWorkHours;
}
function computeMonthlyWorkHours(weeklyWorkdays, dailyWorkHours, dailyCommuteMinutes, vacationDays) {
return ((DAYS_IN_YEAR - vacationDays) / MONTHS_IN_YEAR / DAYS_IN_WEEK) * weeklyWorkdays * (dailyWorkHours + (dailyCommuteMinutes / MINUTES_IN_HOUR));
}
function parsePrice(priceStr) {
const [currencySymbol] = priceStr.match(/[£€$¥₹₽]|Rs\.?/) || [null];
let cleaned = removeCurrencySymbols(priceStr);
cleaned = normalizeDelimiters(cleaned);
return {
value: parseFloat(cleaned),
currency: currencySymbol
};
}
function removeCurrencySymbols(priceStr) {
return priceStr.replace(/[£€$¥₹₽]|Rs\.?/g, "").trim();
}
function normalizeDelimiters(priceStr) {
let cleaned = priceStr.replace(/[£€$¥₹₽]|Rs\.?/g, "").trim();
cleaned = cleaned.replace(/ /g, "");
if (cleaned.includes(".") && cleaned.includes(",")) {
if (cleaned.lastIndexOf(".") > cleaned.lastIndexOf(",")) {
// Dot is the decimal, comma is the thousands separator.
cleaned = cleaned.replace(/,/g, "");
} else {
// Comma is the decimal, dot is the thousands separator.
cleaned = cleaned.replace(/\./g, "").replace(/,/g, ".");
}
} else if (cleaned.includes(",")) {
if ((cleaned.match(/,/g) || []).length > 1 || cleaned.endsWith(",")) {
// Comma is the thousands separator.
cleaned = cleaned.replace(/,/g, "");
} else {
// Comma is the decimal separator.
cleaned = cleaned.replace(/,/g, ".");
}
} else if (cleaned.includes(".")) {
const dotPosition = cleaned.indexOf(".");
if ((cleaned.match(/\./g) || []).length > 1 || cleaned.endsWith(".") || cleaned.substring(dotPosition + 1).length > 2) {
// Dot is the thousands separator.
cleaned = cleaned.replace(/\./g, "");
}
// If it's just one dot, not at the end, and less than 3 digits after it, it's a decimal.
}
return cleaned;
}
function convertToEUR(amount, currency) {
return amount * (CONVERSION_RATES_TO_EUR[currency] || 1);
}
async function replaceText(node) {
if (node.nodeType === Node.TEXT_NODE && !(node.parentNode && node.parentNode.nodeName === 'TEXTAREA')) {
const originalContent = node.dataOriginalContent || node.textContent;
const replacedContent = originalContent.replace(PRICE_REGEX, match => {
const { value, currency } = parsePrice(match);
const convertedValue = convertToEUR(value, currency) / hourWorth;
return `${convertedValue.toFixed(2)} h`;
});
if (replacedContent !== originalContent) {
node.dataOriginalContent = originalContent;
node.textContent = replacedContent;
}
} else {
Array.from(node.childNodes).forEach(replaceText);
}
}
function revertToOriginalText(node) {
if (node.nodeType === Node.TEXT_NODE && node.dataOriginalContent) {
node.textContent = node.dataOriginalContent;
node.dataOriginalContent = null;
} else {
Array.from(node.childNodes).forEach(revertToOriginalText);
}
}
async function isExtensionEnabled() {
const { settings } = await browser.storage.local.get('settings');
return settings && settings['onOffSwitch'];
}
async function changeValues() {
if (await isExtensionEnabled()) {
await refreshHourWorth();
replaceText(document.body);
} else {
revertToOriginalText(document.body);
}
}
changeValues();
browser.runtime.onMessage.addListener(async request => {
if (request.action === "settingsUpdated") {
await changeValues();
}
});
const observer = new MutationObserver(mutations => {
mutations.forEach(mutation => {
if (mutation.addedNodes) {
Array.from(mutation.addedNodes).forEach(replaceText);
}
});
});
observer.observe(document.body, { childList: true, subtree: true });