Skip to content
Snippets Groups Projects
Commit 4ff2d02f authored by Igor Markin's avatar Igor Markin
Browse files

Add build

parent 8e8151c9
No related branches found
No related tags found
No related merge requests found
Showing
with 3603 additions and 1 deletion
node_modules
.idea
yarn-error.log
dist
diff1 0 → 100644
This diff is collapsed.
diff2 0 → 100644
This diff is collapsed.
export declare const normalizeVendorHtml: (document: HTMLDocument, vendor: string) => string;
export declare const printHtmlChildren: (node: Node, printFunction: (node: Node) => string, depth: number) => string;
export declare const printHtmlNode: (node: Node, printFunction: (node: Node) => string, depth: number) => string;
export declare const cleanupHtmlNodeAttributes: (node: Node, cleanupElementAttributes: (element: HTMLElement) => void) => void;
export declare const pruneHtmlNode: (node: Node, pruneElement: (element: HTMLElement) => boolean) => boolean;
export declare const escapeHtmlString: (string: string) => string;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.escapeHtmlString = exports.pruneHtmlNode = exports.cleanupHtmlNodeAttributes = exports.printHtmlNode = exports.printHtmlChildren = exports.normalizeVendorHtml = void 0;
const constants_1 = require("../constants");
const outlook_1 = require("./strategies/outlook");
const constants_2 = require("../constants");
const utils_1 = require("../utils");
const gmail_1 = require("./strategies/gmail");
const nodesAmendingFunctions = {
[constants_2.EMAIL_VENDORS.GMAIL]: gmail_1.amendGmailNodes,
[constants_2.EMAIL_VENDORS.OUTLOOK]: outlook_1.amendOutlookNodes,
};
const nodesPruningFunctions = {
[constants_2.EMAIL_VENDORS.GMAIL]: gmail_1.pruneGmailElement,
[constants_2.EMAIL_VENDORS.OUTLOOK]: outlook_1.pruneOutlookElement,
};
const attributesCleanupFunctions = {
[constants_2.EMAIL_VENDORS.GMAIL]: gmail_1.cleanupGMailElementAttributes,
[constants_2.EMAIL_VENDORS.OUTLOOK]: outlook_1.cleanupOutlookElementAttributes,
};
const vendorPrintingFunctions = {
[constants_2.EMAIL_VENDORS.OUTLOOK]: outlook_1.printOutlookElement,
};
const normalizeVendorHtml = (document, vendor) => {
const mimeBody = document.body;
const amendNodesFunction = nodesAmendingFunctions[vendor];
if (amendNodesFunction) {
amendNodesFunction(document);
}
/**
* Remove unnecessary nodes
*/
const elementPruningFunction = nodesPruningFunctions[vendor];
if (!elementPruningFunction) {
throw new Error(`Vendor "${vendor}" is not supported. Please, develop a pruning function for it.`);
}
exports.pruneHtmlNode(document, elementPruningFunction);
/**
* Cleanup unnecessary attributes of nodes
*/
const elementAttributesCleanupFunction = attributesCleanupFunctions[vendor];
if (elementAttributesCleanupFunction) {
exports.cleanupHtmlNodeAttributes(document, elementAttributesCleanupFunction);
}
/**
* Print nodes
*/
const vendorPrintFunction = vendorPrintingFunctions[vendor];
return exports.printHtmlChildren(mimeBody, vendorPrintFunction, 0);
};
exports.normalizeVendorHtml = normalizeVendorHtml;
const printHtmlChildren = (node, printFunction, depth) => {
let child = node.firstChild;
if (!child) {
return "";
}
if (child == node.lastChild && child.nodeType == constants_1.TEXT_NODE) {
return exports.printHtmlNode(child, printFunction, depth);
}
else {
let result = "";
while (child) {
result = result.concat(exports.printHtmlNode(child, printFunction, depth));
child = child.nextSibling;
}
return result;
}
};
exports.printHtmlChildren = printHtmlChildren;
const printHtmlNode = (node, printFunction, depth) => {
let result = "";
if (printFunction) {
const customPrintout = printFunction(node);
if (customPrintout) {
return customPrintout;
}
}
switch (node.nodeType) {
case constants_1.TEXT_NODE: {
const text = utils_1.removeSpacesAndLinebreaks(node.textContent);
if (text.length) {
result += "<TEXT>";
result += text;
result += "</TEXT>";
result += "\n";
}
break;
}
case constants_1.DOCUMENT_NODE:
result += exports.printHtmlChildren(node, printFunction, depth);
break;
case constants_1.ELEMENT_NODE:
result += "<" + node.nodeName;
Array.from(node.attributes)
.sort((a, b) => a.name.localeCompare(b.name))
.forEach((attribute) => {
result += ` ${attribute.name}`;
if (attribute.value) {
result += `="${exports.escapeHtmlString(attribute.value)}"`;
}
});
if (node.firstChild) {
result += ">";
result += "\n";
result += exports.printHtmlChildren(node, printFunction, depth + 1);
result += "</" + node.nodeName + ">";
}
else {
result += "/>";
}
result += "\n";
break;
}
return result;
};
exports.printHtmlNode = printHtmlNode;
const cleanupHtmlNodeAttributes = (node, cleanupElementAttributes) => {
if (node.nodeType === node.ELEMENT_NODE) {
cleanupElementAttributes(node);
}
let child = node.firstChild;
while (child) {
exports.cleanupHtmlNodeAttributes(child, cleanupElementAttributes);
child = child.nextSibling;
}
};
exports.cleanupHtmlNodeAttributes = cleanupHtmlNodeAttributes;
const pruneHtmlNode = (node, pruneElement) => {
let toBeRemoved = false;
switch (node.nodeType) {
case node.COMMENT_NODE:
case node.DOCUMENT_TYPE_NODE:
toBeRemoved = true;
break;
case node.TEXT_NODE: {
const trimmedText = node.textContent.trim();
if (trimmedText === "") {
toBeRemoved = true;
}
else {
node.textContent = trimmedText;
}
break;
}
case node.ELEMENT_NODE:
toBeRemoved = pruneElement(node);
}
if (toBeRemoved) {
return true;
}
const childrenToRemove = [];
let child = node.firstChild;
while (child) {
exports.pruneHtmlNode(child, pruneElement) && childrenToRemove.push(child);
child = child.nextSibling;
}
childrenToRemove.forEach((child) => node.removeChild(child));
return false;
};
exports.pruneHtmlNode = pruneHtmlNode;
const escapeHtmlString = (string) => {
const matchHtmlRegExp = /["'&<>]/;
const str = "" + string;
const match = matchHtmlRegExp.exec(str);
if (!match) {
return str;
}
let escape;
let html = "";
let index = 0;
let lastIndex = 0;
for (let index = match.index; index < str.length; index++) {
switch (str.charCodeAt(index)) {
case 34: // "
escape = "&quot;";
break;
case 38: // &
escape = "&amp;";
break;
case 39: // '
escape = "&#39;";
break;
case 60: // <
escape = "&lt;";
break;
case 62: // >
escape = "&gt;";
break;
default:
continue;
}
if (lastIndex !== index) {
html += str.substring(lastIndex, index);
}
lastIndex = index + 1;
html += escape;
}
return lastIndex !== index ? html + str.substring(lastIndex, index) : html;
};
exports.escapeHtmlString = escapeHtmlString;
declare const _default: {
normalizeVendorHtml: (document: HTMLDocument, vendor: string) => string;
};
export default _default;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const HTMLNormalizer_1 = require("./HTMLNormalizer");
exports.default = {
normalizeVendorHtml: HTMLNormalizer_1.normalizeVendorHtml,
};
export declare const ELEMENT_TYPES_TO_REMOVE: {
br: boolean;
hr: boolean;
use: boolean;
svg: boolean;
};
export declare const ATTRIBUTES_TO_KEEP: {
alt: boolean;
src: boolean;
cite: boolean;
data: boolean;
datetime: boolean;
href: boolean;
value: boolean;
};
/**
* Decides whether node should be removed
* @param element
*/
export declare const pruneElement: (element: HTMLElement) => boolean;
export declare const cloneAnchorFromPane: (a: HTMLAnchorElement, pane: HTMLElement) => void;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.cloneAnchorFromPane = exports.pruneElement = exports.ATTRIBUTES_TO_KEEP = exports.ELEMENT_TYPES_TO_REMOVE = void 0;
const DUMMY_QR_CODE_ID = "dummyQrCode";
exports.ELEMENT_TYPES_TO_REMOVE = { br: true, hr: true, use: true, svg: true };
exports.ATTRIBUTES_TO_KEEP = {
alt: true,
src: true,
cite: true,
data: true,
datetime: true,
href: true,
value: true,
};
/**
* Removes dummy QR code from HTML
* @param element
*/
const isDummyQrCode = (element) => {
if (element.id === DUMMY_QR_CODE_ID) {
return true;
}
};
/**
* Decides whether node should be removed
* @param element
*/
const pruneElement = (element) => {
if (isDummyQrCode(element)) {
return true;
}
if (element.nodeName.toLowerCase() === "div" &&
element.childNodes.length === 0) {
return true;
}
return !!exports.ELEMENT_TYPES_TO_REMOVE[element.nodeName.toLowerCase()];
};
exports.pruneElement = pruneElement;
const cloneAnchorFromPane = (a, pane) => {
try {
const url = new URL(a.href);
// If this is external url
if (url.host && url.protocol) {
pane.parentNode.insertBefore(a.cloneNode(false), pane);
}
}
catch {
return;
}
};
exports.cloneAnchorFromPane = cloneAnchorFromPane;
export declare const pruneGmailElement: (element: HTMLElement) => boolean;
export declare const amendGmailNodes: (document: HTMLDocument) => void;
export declare const cleanupGMailElementAttributes: (element: HTMLElement) => void;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.cleanupGMailElementAttributes = exports.amendGmailNodes = exports.pruneGmailElement = void 0;
const common_1 = require("./common");
const pruneGmailElement = (element) => {
return common_1.pruneElement(element);
};
exports.pruneGmailElement = pruneGmailElement;
const amendGmailNodes = (document) => {
/**
* Look for attachments panes and remove everything but liks
*/
const attachmentsPanes = Array.from(document.getElementsByClassName("gmail_chip"));
attachmentsPanes.forEach((pane) => {
const as = pane.querySelectorAll("a");
as.forEach((a) => {
common_1.cloneAnchorFromPane(a, pane);
});
});
attachmentsPanes.forEach((pane) => {
pane.parentNode.removeChild(pane);
});
};
exports.amendGmailNodes = amendGmailNodes;
const cleanupGMailElementAttributes = (element) => {
if (element.attributes.length > 0) {
for (const attribute of element.attributes) {
if (attribute.name === "data-surl") {
element.setAttribute("src", attribute.value);
}
}
for (let i = 0; i < element.attributes.length; i++) {
const attribute = element.attributes[i];
if (!common_1.ATTRIBUTES_TO_KEEP[attribute.name]) {
element.removeAttribute(attribute.name);
i--;
}
}
}
};
exports.cleanupGMailElementAttributes = cleanupGMailElementAttributes;
export declare const printOutlookElement: (node: Node) => string;
/**
* Returns true if element should be completely removed
* @param element
*/
export declare const pruneOutlookElement: (element: HTMLElement) => boolean;
export declare const amendOutlookNodes: (document: HTMLDocument) => void;
export declare const cleanupOutlookElementAttributes: (element: HTMLElement) => void;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.cleanupOutlookElementAttributes = exports.amendOutlookNodes = exports.pruneOutlookElement = exports.printOutlookElement = void 0;
// TODO: Move this logic to amendOutlookNodes
const HTMLNormalizer_1 = require("../HTMLNormalizer");
const constants_1 = require("../../constants");
const common_1 = require("./common");
const printOutlookElement = (node) => {
if (node.nodeType === constants_1.ELEMENT_NODE) {
if (node.classList.contains("WordSection1")) {
return HTMLNormalizer_1.printHtmlChildren(node, null, 0);
}
}
};
exports.printOutlookElement = printOutlookElement;
/**
* Returns true if element should be completely removed
* @param element
*/
const pruneOutlookElement = (element) => {
if (common_1.pruneElement(element)) {
return true;
}
// Remove Outlook generic <o:*> tags
return !!element.nodeName.toLowerCase().startsWith("o:");
};
exports.pruneOutlookElement = pruneOutlookElement;
const amendOutlookNodes = (document) => {
/**
* Remove quoted text
*/
// Quoted text in web apps
const appendOnSend = document.querySelector("[id*='appendonsend']");
if (appendOnSend) {
let child = appendOnSend;
while (child) {
const nextSibling = child.nextSibling;
child.parentNode.removeChild(child);
child = nextSibling;
}
}
// Quoted text in desktop apps
// let mailOriginal = document.querySelector("[name*='_MailOriginal']") as HTMLElement;
// if (mailOriginal) {
// let removeCurrent = true;
// while (mailOriginal !== document.body) {
// while (mailOriginal.nextSibling) {
// mailOriginal.nextSibling.remove();
// }
// const currentNode = mailOriginal;
// mailOriginal = mailOriginal.parentElement;
// if (removeCurrent && currentNode.previousSibling) {
// currentNode.remove();
// removeCurrent = false;
// }
// }
// }
// if (mailOriginal) {
// const separatorCandidate = mailOriginal.parentNode as Node;
//
// // while (!(separatorCandidate.parentNode as Element).classList.contains("WordSection1")) {
// // separatorCandidate = separatorCandidate.parentNode;
// // }
//
// let child = separatorCandidate;
// while (child) {
// const nextSibling = child.nextSibling;
// child.parentNode.removeChild(child);
// child = nextSibling as Node;
// }
// }
/**
* Get rid of attachments panes
*/
const attachmentsPanesContainer = document.getElementById("OwaReferenceAttachments");
const attachmentsPanesContainerEnd = document.getElementById("OwaReferenceAttachmentsEnd");
if (attachmentsPanesContainer) {
const as = attachmentsPanesContainer.getElementsByTagName("a");
Array.from(as).forEach((a) => {
common_1.cloneAnchorFromPane(a, attachmentsPanesContainer);
});
attachmentsPanesContainer.parentNode.removeChild(attachmentsPanesContainer);
}
attachmentsPanesContainerEnd &&
attachmentsPanesContainerEnd.parentNode.removeChild(attachmentsPanesContainerEnd);
/**
* Unwind spans, because sometimes Outlook wraps everything into span after sending
*/
const spans = document.getElementsByTagName("span");
//Sort spans by depth to start unwinding the deepest ones, which does not contain nested spans
const spansDepths = {};
Array.from(spans).forEach((span) => {
let descendant = span;
let parent = descendant.parentNode;
let depth = 0;
while (parent && descendant !== parent) {
descendant = parent;
parent = descendant.parentNode;
depth++;
}
if (!spansDepths[depth]) {
spansDepths[depth] = [];
}
spansDepths[depth].push(span);
});
Object.keys(spansDepths)
.sort((a, b) => parseInt(b) - parseInt(a))
.forEach((depth) => {
spansDepths[depth].forEach((span) => {
let child = span.firstChild;
const parent = span.parentNode;
while (child) {
parent.insertBefore(child.cloneNode(true), span);
child = child.nextSibling;
}
span.parentNode.removeChild(span);
});
});
};
exports.amendOutlookNodes = amendOutlookNodes;
const cleanupOutlookElementAttributes = (element) => {
if (element.attributes.length > 0) {
for (const attribute of element.attributes) {
let valueSplit = attribute.value.split(" ");
valueSplit = valueSplit.map((value) => value.startsWith("x_") ? value.replace("x_", "") : value);
element.setAttribute(attribute.name, valueSplit.join(" "));
}
for (let i = 0; i < element.attributes.length; i++) {
const attribute = element.attributes[i];
if (!common_1.ATTRIBUTES_TO_KEEP[attribute.name]) {
element.removeAttribute(attribute.name);
i--;
}
}
}
};
exports.cleanupOutlookElementAttributes = cleanupOutlookElementAttributes;
export declare const ELEMENT_NODE = 1;
export declare const TEXT_NODE = 3;
export declare const DOCUMENT_NODE = 9;
export declare const EMAIL_VENDORS: {
GMAIL: string;
OUTLOOK: string;
ROUNDCUBE: string;
GENERIC_MIME: string;
};
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.EMAIL_VENDORS = exports.DOCUMENT_NODE = exports.TEXT_NODE = exports.ELEMENT_NODE = void 0;
exports.ELEMENT_NODE = 1;
exports.TEXT_NODE = 3;
exports.DOCUMENT_NODE = 9;
exports.EMAIL_VENDORS = {
GMAIL: "GMAIL",
OUTLOOK: "OUTLOOK",
ROUNDCUBE: "ROUNDCUBE",
GENERIC_MIME: "GENERIC_MIME",
};
export { default as HTMLNormalizer } from "./HTMLNormalizer";
export { default as PlainNormalizer } from "./PlainNormalizer";
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PlainNormalizer = exports.HTMLNormalizer = void 0;
var HTMLNormalizer_1 = require("./HTMLNormalizer");
Object.defineProperty(exports, "HTMLNormalizer", { enumerable: true, get: function () { return HTMLNormalizer_1.default; } });
var PlainNormalizer_1 = require("./PlainNormalizer");
Object.defineProperty(exports, "PlainNormalizer", { enumerable: true, get: function () { return PlainNormalizer_1.default; } });
export declare const removeSpacesAndLinebreaks: (s: string) => string;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.removeSpacesAndLinebreaks = void 0;
const removeSpacesAndLinebreaks = (s) => {
const regexNewlines = new RegExp(/[\r\n\v]+/g);
const regexSpaces = new RegExp(/\s+|\u200B/g);
return s.replace(regexNewlines, "").replace(regexSpaces, "");
};
exports.removeSpacesAndLinebreaks = removeSpacesAndLinebreaks;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment