@salesflare/planer
Advanced tools
Comparing version 1.1.3 to 2.0.0
@@ -1,2 +0,2 @@ | ||
// Generated by CoffeeScript 1.12.7 | ||
// Generated by CoffeeScript 2.5.1 | ||
(function() { | ||
@@ -9,16 +9,20 @@ var BREAK_TAG_REGEX, CHECKPOINT_PREFIX, CHECKPOINT_SUFFIX, DOCUMENT_POSITION_FOLLOWING, DOCUMENT_POSITION_PRECEDING, OUTLOOK_SPLITTER_QUERY_SELECTORS, OUTLOOK_SPLITTER_QUOTE_IDS, OUTLOOK_XPATH_SPLITTER_QUERIES, QUOTE_IDS, compareByDomPosition, elementIsAllContent, ensureTextNodeBetweenChildElements, findMicrosoftSplitter, findOutlookSplitterWithQuerySelector, findOutlookSplitterWithQuoteId, findOutlookSplitterWithXpathQuery, findParentDiv, hasTagName, isTextNodeWrappedInSpan, removeNodes; | ||
exports.CHECKPOINT_PATTERN = new RegExp(CHECKPOINT_PREFIX + "\\d+" + CHECKPOINT_SUFFIX, 'g'); | ||
exports.CHECKPOINT_PATTERN = new RegExp(`${CHECKPOINT_PREFIX}\\d+${CHECKPOINT_SUFFIX}`, 'g'); | ||
// HTML quote indicators (tag ids) | ||
QUOTE_IDS = ['OLK_SRC_BODY_SECTION']; | ||
// Create an instance of Document using the message html and the injected base document | ||
exports.createEmailDocument = function(msgBody, dom) { | ||
var emailBodyElement, emailDocument, head, htmlElement; | ||
emailDocument = dom.implementation.createHTMLDocument(); | ||
htmlElement = emailDocument.getElementsByTagName('html')[0]; | ||
// Write html of email to `html` element | ||
[htmlElement] = emailDocument.getElementsByTagName('html'); | ||
htmlElement.innerHTML = msgBody.trim(); | ||
if (emailDocument.body == null) { | ||
emailBodyElement = emailDocument.getElementsByTagName('body')[0]; | ||
[emailBodyElement] = emailDocument.getElementsByTagName('body'); | ||
emailDocument.body = emailBodyElement; | ||
} | ||
head = emailDocument.getElementsByTagName('head')[0]; | ||
// Remove 'head' element from document | ||
[head] = emailDocument.getElementsByTagName('head'); | ||
if (head) { | ||
@@ -30,12 +34,17 @@ emailDocument.documentElement.removeChild(head); | ||
// Recursively adds checkpoints to html tree. | ||
exports.addCheckpoints = function(htmlNode, counter) { | ||
var childNode, i, len, ref; | ||
// 3 is a text node | ||
if (htmlNode.nodeType === 3) { | ||
htmlNode.nodeValue = "" + (htmlNode.nodeValue.trim()) + CHECKPOINT_PREFIX + counter + CHECKPOINT_SUFFIX + "\n"; | ||
htmlNode.nodeValue = `${htmlNode.nodeValue.trim()}${CHECKPOINT_PREFIX}${counter}${CHECKPOINT_SUFFIX}\n`; | ||
counter++; | ||
} | ||
// 1 is an element | ||
if (htmlNode.nodeType === 1) { | ||
if (!hasTagName(htmlNode, 'body')) { | ||
htmlNode.innerHTML = " " + htmlNode.innerHTML + " "; | ||
// Pad with spacing to ensure there are text nodes at the begining and end of non-body elements | ||
htmlNode.innerHTML = ` ${htmlNode.innerHTML} `; | ||
} | ||
// Ensure that there are text nodes between sibling elements | ||
ensureTextNodeBetweenChildElements(htmlNode); | ||
@@ -52,4 +61,5 @@ ref = htmlNode.childNodes; | ||
exports.deleteQuotationTags = function(htmlNode, counter, quotationCheckpoints) { | ||
var childNode, childTagInQuotation, i, j, len, len1, quotationChildren, ref, ref1, tagInQuotation; | ||
var childNode, childTagInQuotation, i, j, len, len1, quotationChildren, ref, tagInQuotation; | ||
tagInQuotation = true; | ||
// 3 is a text node | ||
if (htmlNode.nodeType === 3) { | ||
@@ -62,8 +72,12 @@ if (!quotationCheckpoints[counter]) { | ||
} | ||
// 1 is an element | ||
if (htmlNode.nodeType === 1) { | ||
// Collect child nodes that are marked as in the quotation | ||
childTagInQuotation = false; | ||
quotationChildren = []; | ||
if (!hasTagName(htmlNode, 'body')) { | ||
htmlNode.innerHTML = " " + htmlNode.innerHTML + " "; | ||
// Pad with spacing to ensure there are text nodes at the begining and end of non-body elements | ||
htmlNode.innerHTML = ` ${htmlNode.innerHTML} `; | ||
} | ||
// Ensure that there are text nodes between sibling elements | ||
ensureTextNodeBetweenChildElements(htmlNode); | ||
@@ -73,3 +87,4 @@ ref = htmlNode.childNodes; | ||
childNode = ref[i]; | ||
ref1 = exports.deleteQuotationTags(childNode, counter, quotationCheckpoints), counter = ref1[0], childTagInQuotation = ref1[1]; | ||
[counter, childTagInQuotation] = exports.deleteQuotationTags(childNode, counter, quotationCheckpoints); | ||
// Keep tracking if all children are in the quotation | ||
tagInQuotation = tagInQuotation && childTagInQuotation; | ||
@@ -81,5 +96,7 @@ if (childTagInQuotation) { | ||
} | ||
// If all of an element's children are part of a quotation, let parent delete whole element | ||
if (tagInQuotation) { | ||
return [counter, tagInQuotation]; | ||
} else { | ||
// Otherwise, delete specific quotation children | ||
for (j = 0, len1 = quotationChildren.length; j < len1; j++) { | ||
@@ -119,2 +136,3 @@ childNode = quotationChildren[j]; | ||
// Remove the last non-nested blockquote element | ||
exports.cutBlockQuote = function(emailDocument) { | ||
@@ -150,4 +168,6 @@ var blockquoteElement, div, parent, xpathQuery, xpathResult; | ||
var afterSplitter, fromBlock, lastBlock, parentDiv, ref, splitterElement, textNode, xpathQuery, xpathResult; | ||
// Handle case where From: block is enclosed in a tag | ||
xpathQuery = "//*[starts-with(normalize-space(.), 'From:')]|//*[starts-with(normalize-space(.), 'Date:')]"; | ||
xpathResult = emailDocument.evaluate(xpathQuery, emailDocument, null, 5, null); | ||
// Find last element in iterator | ||
while (fromBlock = xpathResult.iterateNext()) { | ||
@@ -157,2 +177,3 @@ lastBlock = fromBlock; | ||
if (lastBlock != null) { | ||
// Find parent div and remove from document | ||
parentDiv = findParentDiv(lastBlock); | ||
@@ -164,4 +185,6 @@ if ((parentDiv != null) && !elementIsAllContent(parentDiv)) { | ||
} | ||
// Handle the case when From: block goes right after e.g. <hr> and is not enclosed in a tag itself | ||
xpathQuery = "//text()[starts-with(normalize-space(.), 'From:')]|//text()[starts-with(normalize-space(.), 'Date:')]"; | ||
xpathResult = emailDocument.evaluate(xpathQuery, emailDocument, null, 9, null); | ||
// The text node that is the result | ||
textNode = xpathResult.singleNodeValue; | ||
@@ -172,4 +195,7 @@ if (textNode == null) { | ||
if (isTextNodeWrappedInSpan(textNode)) { | ||
// The text node is wrapped in a span element. All sorts formatting could be happening here. | ||
// Return false and hope plain text algorithm can figure it out. | ||
return false; | ||
} | ||
// The previous sibling stopped the initial xpath query from working, so it is likely a splitter (like an hr) | ||
splitterElement = textNode.previousSibling; | ||
@@ -181,2 +207,3 @@ if (splitterElement != null) { | ||
} | ||
// Remove all subsequent siblings of the textNode | ||
afterSplitter = textNode.nextSibling; | ||
@@ -222,2 +249,4 @@ while (afterSplitter != null) { | ||
// Queries to find a splitter that's the only child of a single parent div | ||
// Usually represents the dividing line between messages in the Outlook html | ||
OUTLOOK_SPLITTER_QUERY_SELECTORS = { | ||
@@ -229,2 +258,3 @@ outlook2007: "div[style='border:none;border-top:solid #B5C4DF 1.0pt;padding:3.0pt 0cm 0cm 0cm']", | ||
// More complicated Xpath queries for versions of Outlook that don't use the dividing lines | ||
OUTLOOK_XPATH_SPLITTER_QUERIES = { | ||
@@ -234,3 +264,5 @@ outlook2003: "//div/div[@class='MsoNormal' and @align='center' and @style='text-align:center']/font/span/hr[@size='3' and @width='100%' and @align='center' and @tabindex='-1']" | ||
// For more modern versions of Outlook that contain replies in quote block with an id | ||
OUTLOOK_SPLITTER_QUOTE_IDS = { | ||
// There's potentially multiple elements with this id so we need to cut everything after this quote as well | ||
office365: '#divRplyFwdMsg' | ||
@@ -263,2 +295,3 @@ }; | ||
} | ||
// Find the earliest splitter in the DOM to remove everything after it | ||
return possibleSplitterElements.sort(compareByDomPosition)[0]; | ||
@@ -286,2 +319,3 @@ }; | ||
splitterElement = xpathResult.singleNodeValue; | ||
// Go up the tree to find the enclosing div. | ||
if (splitterElement != null) { | ||
@@ -319,3 +353,3 @@ splitterElement = splitterElement.parentElement.parentElement; | ||
results = []; | ||
for (index = i = ref = nodesArray.length - 1; ref <= 0 ? i <= 0 : i >= 0; index = ref <= 0 ? ++i : --i) { | ||
for (index = i = ref = nodesArray.length - 1; (ref <= 0 ? i <= 0 : i >= 0); index = ref <= 0 ? ++i : --i) { | ||
node = nodesArray[index]; | ||
@@ -338,2 +372,3 @@ results.push(node != null ? (ref1 = node.parentNode) != null ? ref1.removeChild(node) : void 0 : void 0); | ||
while (currentNode.nextSibling) { | ||
// An element is followed by an element | ||
if (currentNode.nodeType === 1 && currentNode.nextSibling.nodeType === 1) { | ||
@@ -340,0 +375,0 @@ newTextNode = dom.createTextNode(' '); |
@@ -1,2 +0,2 @@ | ||
// Generated by CoffeeScript 1.12.7 | ||
// Generated by CoffeeScript 2.5.1 | ||
(function() { | ||
@@ -15,9 +15,11 @@ var CONTENT_CHUNK_SIZE, MAX_LINES_COUNT, MAX_LINE_LENGTH, REGEXES, SPLITTER_MAX_LINES, _CRLF_to_LF, _restore_CRLF, getDelimiter, htmlPlaner, isSplitter, postprocess, preprocess, setReturnFlags; | ||
exports.extractFrom = function(msgBody, contentType, dom) { | ||
if (contentType == null) { | ||
contentType = 'text/plain'; | ||
} | ||
if (dom == null) { | ||
dom = null; | ||
} | ||
// Extract actual message from email. | ||
// Will use provided `contentType` to decide which algorithm to use (plain text or html). | ||
// @param msgBody [String] the html content of the email | ||
// @param contentType [String] the contentType of the email. Only `text/plain` and `text/html` are supported. | ||
// @param dom [Document] the document object to use for html parsing. | ||
// @return [String] the text/html of the actual message without quotations | ||
exports.extractFrom = function(msgBody, contentType = 'text/plain', dom = null) { | ||
if (contentType === 'text/plain') { | ||
@@ -33,2 +35,13 @@ return exports.extractFromPlain(msgBody); | ||
// Extract actual message from provided textual email. | ||
// Store delimiter used by the email (\n or \r\n), | ||
// split the email into lines, | ||
// use regexes to mark each line as either part of the message or quotation, | ||
// remove lines that are part of the quotation, | ||
// put message back together using the saved delimeter, | ||
// remove changes made by algorithm. | ||
// @param msgBody [String] the html content of the email | ||
// @return [String] the text of the message without quotations | ||
exports.extractFromPlain = function(msgBody) { | ||
@@ -46,4 +59,25 @@ var delimiter, lines, markers; | ||
// Extract actual message from provided html message body | ||
// using tags and plain text algorithm. | ||
// Cut out the 'blockquote', 'gmail_quote' tags. | ||
// Cut out Microsoft (Outlook, Windows mail) quotations. | ||
// Then use plain text algorithm to cut out splitter or | ||
// leftover quotation. | ||
// This works by adding checkpoint text to all html tags, | ||
// then converting html to text, | ||
// then extracting quotations from text, | ||
// then checking deleted checkpoints, | ||
// then deleting necessary tags. | ||
// Will use the document provided to create a new document using: | ||
// Document.implementation.createHTMLDocument() | ||
// @param msgBody [String] the html content of the email | ||
// @param dom [Document] a document object or equivalent implementation. | ||
// Must respond to `DOMImplementation.createHTMLDocument()`. | ||
// @see https://developer.mozilla.org/en-US/docs/Web/API/DOMImplementation/createHTMLDocument | ||
exports.extractFromHtml = function(msgBody, dom) { | ||
var checkpoint, crlfReplaced, emailDocument, emailDocumentCopy, haveCutQuotations, i, index, k, l, len, len1, line, lineCheckpoints, lines, m, markers, matches, numberOfCheckpoints, plainTextMsg, quotationCheckpoints, ref, ref1, ref2, ref3, returnFlags; | ||
var checkpoint, crlfReplaced, emailDocument, emailDocumentCopy, haveCutQuotations, i, index, k, l, len, len1, line, lineCheckpoints, lines, m, markers, matches, numberOfCheckpoints, plainTextMsg, quotationCheckpoints, ref, ref1, ref2, returnFlags; | ||
if (dom == null) { | ||
@@ -56,6 +90,12 @@ console.error("No dom provided to parse html."); | ||
} | ||
ref = _CRLF_to_LF(msgBody), msgBody = ref[0], crlfReplaced = ref[1]; | ||
[msgBody, crlfReplaced] = _CRLF_to_LF(msgBody); | ||
emailDocument = htmlPlaner.createEmailDocument(msgBody, dom); | ||
// TODO: this check does not handle cases of emails between various email providers well because | ||
// it will find whichever splitter comes first in this list, not necessarily the top-most and stop | ||
// checking for others. Possible solution is to use something like compareByDomPosition from htmlPlaner | ||
// to find the earliest splitter in the DOM. | ||
haveCutQuotations = htmlPlaner.cutGmailQuote(emailDocument) || htmlPlaner.cutBlockQuote(emailDocument) || htmlPlaner.cutMicrosoftQuote(emailDocument) || htmlPlaner.cutById(emailDocument) || htmlPlaner.cutFromBlock(emailDocument); | ||
// Create unaltered copy of email document | ||
emailDocumentCopy = htmlPlaner.createEmailDocument(emailDocument.documentElement.outerHTML, dom); | ||
// Add checkpoints to html document | ||
numberOfCheckpoints = htmlPlaner.addCheckpoints(emailDocument.body, 0); | ||
@@ -65,2 +105,3 @@ quotationCheckpoints = Array.apply(null, Array(numberOfCheckpoints)).map(function() { | ||
}); | ||
// Get plain text version to put through plain text algorithm | ||
htmlPlaner.replaceBreakTagsWithLineFeeds(emailDocument); | ||
@@ -73,2 +114,3 @@ plainTextMsg = emailDocument.body.textContent; | ||
} | ||
// Collect checkpoints for each line | ||
lineCheckpoints = new Array(lines.length); | ||
@@ -82,2 +124,3 @@ for (index = k = 0, len = lines.length; k < len; index = ++k) { | ||
} | ||
// Remove checkpoints from lines to pass through plain text algorithm | ||
lines = lines.map(function(line) { | ||
@@ -91,17 +134,21 @@ return line.replace(htmlPlaner.CHECKPOINT_PATTERN, ''); | ||
if (haveCutQuotations) { | ||
// If we cut a quotation element out of the html, return the html output of the copied document. | ||
return _restore_CRLF(emailDocumentCopy.documentElement.outerHTML, crlfReplaced); | ||
} else { | ||
// There was nothing to remove, return original message. | ||
return msgBody; | ||
} | ||
} | ||
for (i = l = ref1 = returnFlags.firstLine, ref2 = returnFlags.lastLine; ref1 <= ref2 ? l <= ref2 : l >= ref2; i = ref1 <= ref2 ? ++l : --l) { | ||
// Set quotationCheckpoints to true for checkpoints on lines that were removed | ||
for (i = l = ref = returnFlags.firstLine, ref1 = returnFlags.lastLine; (ref <= ref1 ? l <= ref1 : l >= ref1); i = ref <= ref1 ? ++l : --l) { | ||
if (!lineCheckpoints[i]) { | ||
continue; | ||
} | ||
ref3 = lineCheckpoints[i]; | ||
for (m = 0, len1 = ref3.length; m < len1; m++) { | ||
checkpoint = ref3[m]; | ||
ref2 = lineCheckpoints[i]; | ||
for (m = 0, len1 = ref2.length; m < len1; m++) { | ||
checkpoint = ref2[m]; | ||
quotationCheckpoints[checkpoint] = true; | ||
} | ||
} | ||
// Remove the element that have been identified as part of the quoted message | ||
htmlPlaner.deleteQuotationTags(emailDocumentCopy.body, 0, quotationCheckpoints); | ||
@@ -111,2 +158,14 @@ return emailDocumentCopy.documentElement.outerHTML; | ||
// Mark message lines with markers to distinguish quotation lines. | ||
// Markers: | ||
// * e - empty line | ||
// * f - Forwarded message line, see REGEXES.FWD | ||
// * m - line that starts with quotation marker '>' | ||
// * s - splitter line | ||
// * t - presumably lines from the last message in the conversation | ||
// $> markMessageLines(['answer', 'From: foo@bar.com', '', '> question']) | ||
// 'tsem' | ||
exports.markMessageLines = function(lines) { | ||
@@ -118,12 +177,13 @@ var i, j, k, markers, ref, splitter, splitterLines; | ||
if (lines[i].trim() === '') { | ||
markers[i] = 'e'; | ||
markers[i] = 'e'; // empty line | ||
} else if (REGEXES.QUOT_PATTERN.test(lines[i])) { | ||
markers[i] = 'm'; | ||
markers[i] = 'm'; // line with quotation marker | ||
} else if (REGEXES.FWD.test(lines[i])) { | ||
markers[i] = 'f'; | ||
markers[i] = 'f'; // ---- Forwarded message ---- | ||
} else { | ||
splitter = isSplitter(lines.slice(i, i + SPLITTER_MAX_LINES).join("\n")); | ||
if (splitter) { | ||
// splitter[0] is the entire match | ||
splitterLines = splitter[0].split("\n"); | ||
for (j = k = 0, ref = splitterLines.length; 0 <= ref ? k <= ref : k >= ref; j = 0 <= ref ? ++k : --k) { | ||
for (j = k = 0, ref = splitterLines.length; (0 <= ref ? k <= ref : k >= ref); j = 0 <= ref ? ++k : --k) { | ||
markers[i + j] = 's'; | ||
@@ -141,2 +201,3 @@ } | ||
// Check the line for each splitter regex. | ||
isSplitter = function(line) { | ||
@@ -158,10 +219,18 @@ var k, len, matchArray, pattern, ref; | ||
exports.processMarkedLines = function(lines, markers, returnFlags) { | ||
// Run regexes against message's marked lines to strip quotations. | ||
// Return only last message lines. | ||
// $> processMarkedLines(['Hello', 'From: foo@bar.com', '', '> Hi'], 'tsem']) | ||
// ['Hello'] | ||
// Will also modify the provided returnFlags object and set the following properties: | ||
// returnFlags = { wereLinesDeleted: (true|false), firstLine: (Number), lastLine: (Number) } | ||
// @see setReturnFlags | ||
exports.processMarkedLines = function(lines, markers, returnFlags = {}) { | ||
var inlineMatchRegex, inlineReplyIndex, inlineReplyMatch, isInlineReplyLink, quotationEnd, quotationMatch; | ||
if (returnFlags == null) { | ||
returnFlags = {}; | ||
} | ||
// If there are no splitters there should be no markers | ||
if (markers.indexOf('s') < 0 && !/(me*){3}/.test(markers)) { | ||
markers = markers.replace(/m/g, 't'); | ||
} | ||
// If the message is a forward do nothing. | ||
if (/^[te]*f/.test(markers)) { | ||
@@ -171,2 +240,3 @@ setReturnFlags(returnFlags, false, -1, -1); | ||
} | ||
// Find inline replies (tm's following the first m in markers string) | ||
inlineMatchRegex = new RegExp('m(?=e*((?:t+e*)+)m)', 'g'); | ||
@@ -184,2 +254,3 @@ while (inlineReplyMatch = inlineMatchRegex.exec(lines)) { | ||
} | ||
// Cut out text lines coming after splitter if there are no markers there | ||
quotationMatch = new RegExp('(se*)+((t|f)+e*)+', 'g').exec(markers); | ||
@@ -190,2 +261,3 @@ if (quotationMatch) { | ||
} | ||
// Handle the case with markers | ||
quotationMatch = REGEXES.QUOTATION.exec(markers) || REGEXES.EMPTY_QUOTATION.exec(markers); | ||
@@ -207,19 +279,29 @@ if (quotationMatch) { | ||
preprocess = function(msgBody, delimiter, contentType) { | ||
if (contentType == null) { | ||
contentType = 'text/plain'; | ||
} | ||
// Prepares msgBody for being stripped. | ||
// Replaces link brackets so that they couldn't be taken for quotation marker. | ||
// Splits line in two if splitter pattern preceded by some text on the same | ||
// line (done only for 'On <date> <person> wrote:' pattern). | ||
preprocess = function(msgBody, delimiter, contentType = 'text/plain') { | ||
// Normalize links i.e. replace '<', '>' wrapping the link with some symbols | ||
// so that '>' closing the link couldn't be mistakenly taken for quotation | ||
// marker. | ||
// REGEXES.LINK has 1 captured group | ||
msgBody = msgBody.replace(REGEXES.LINK, function(entireMatch, groupMatch1, matchIndex) { | ||
var newLineIndex; | ||
// Look for closest newline character | ||
newLineIndex = msgBody.lastIndexOf("\n", matchIndex); | ||
// If the new current line starts with a '>' quotation marker, don't mess with the link | ||
if (newLineIndex > 0 && msgBody[newLineIndex + 1] === '>') { | ||
return entireMatch; | ||
} else { | ||
return "@@" + groupMatch1 + "@@"; | ||
return `@@${groupMatch1}@@`; | ||
} | ||
}); | ||
if (contentType === 'text/plain' && msgBody.length < MAX_LINE_LENGTH) { | ||
// ON_DATE_SMB_WROTE has 4 captured groups | ||
msgBody = msgBody.replace(REGEXES.ON_DATE_SMB_WROTE, function(entireMatch, groupMatch1, groupMatch2, groupMatch3, groupMatch4, matchIndex) { | ||
if (matchIndex && msgBody[matchIndex - 1] !== "\n") { | ||
return "" + delimiter + entireMatch; | ||
return `${delimiter}${entireMatch}`; | ||
} else { | ||
@@ -233,2 +315,4 @@ return entireMatch; | ||
// Make up for changes done at preprocessing message. | ||
// Replace link brackets back to '<' and '>'. | ||
postprocess = function(msgBody) { | ||
@@ -265,6 +349,3 @@ return msgBody.replace(REGEXES.NORMALIZED_LINK, '<$1>').trim(); | ||
_restore_CRLF = function(msgBody, replaced) { | ||
if (replaced == null) { | ||
replaced = true; | ||
} | ||
_restore_CRLF = function(msgBody, replaced = true) { | ||
if (replaced) { | ||
@@ -271,0 +352,0 @@ return msgBody.replace(new RegExp('\n', 'g'), '\r\n'); |
@@ -1,2 +0,2 @@ | ||
// Generated by CoffeeScript 1.12.7 | ||
// Generated by CoffeeScript 2.5.1 | ||
(function() { | ||
@@ -7,4 +7,6 @@ exports.DELIMITER = new RegExp('\r?\n'); | ||
// On {date}, {somebody} wrote: | ||
exports.ON_DATE_SMB_WROTE = new RegExp("(-*[>]?[ ]?(On|Le|W dniu|Op|Am|P\xe5|Den)[ ].*(,|u\u017cytkownik)(.*\n){0,2}.*(wrote|sent|a \xe9crit|napisa\u0142|schreef|verzond|geschreven|schrieb|skrev):?-*)"); | ||
// On {date} wrote {somebody}: | ||
exports.ON_DATE_WROTE_SMB = new RegExp('(-*[>]?[ ]?(Op|Am)[ ].*(.*\n){0,2}.*(schreef|verzond|geschreven|schrieb)[ ]*.*:)'); | ||
@@ -11,0 +13,0 @@ |
{ | ||
"name": "@salesflare/planer", | ||
"version": "1.1.3", | ||
"version": "2.0.0", | ||
"description": "Remove reply quotations from emails", | ||
@@ -10,3 +10,3 @@ "main": "lib/planer.js", | ||
"scripts": { | ||
"test": "mocha test/", | ||
"test": "mocha --require coffeescript/register --recursive --reporter spec test/**", | ||
"compile": "coffee -o lib -c src" | ||
@@ -31,7 +31,7 @@ }, | ||
"devDependencies": { | ||
"chai": "^3.4.1", | ||
"coffee-script": "^1.10.0", | ||
"jsdom": "^11.6.0", | ||
"mocha": "^2.3.4" | ||
"chai": "^4.2.0", | ||
"coffeescript": "^2.5.1", | ||
"jsdom": "^16.4.0", | ||
"mocha": "^8.1.3" | ||
} | ||
} |
# planer | ||
Remove reply quotations from emails. | ||
At [lever](https://github.com/lever) we are into simple machines. | ||
At [lever](https://github.com/lever) we are into simple machines. | ||
A planer removes some material to flatten out a rough surface, which seemed appropriate for a module that smooths out an email to extract the actual message. | ||
@@ -10,3 +11,4 @@ | ||
# Installation | ||
## Installation | ||
Use npm to install planer (add `-g` if you would like it to be global): | ||
@@ -16,10 +18,11 @@ | ||
# Usage | ||
## Usage | ||
_Important_: planer accepts an injected [Document](https://developer.mozilla.org/en-US/docs/Web/API/Document) object to perform html parsing. | ||
You can use `window.document` in a browser, or something akin to `jsdom` on the server. | ||
You can use `window.document` in a browser, or something akin to `jsdom` on the server. | ||
We use `jsdom` in our test suite. | ||
To extract the message from a plain text email: | ||
``` | ||
```js | ||
planer = require('planer'); | ||
@@ -33,3 +36,4 @@ | ||
To extract the message from an html email: | ||
``` | ||
```js | ||
planer = require('planer'); | ||
@@ -42,3 +46,3 @@ | ||
# Contributing | ||
## Contributing | ||
@@ -51,5 +55,4 @@ Contributions are of course encouraged. | ||
## MIT Licence | ||
# MIT Licence | ||
Copyright (c) 2015 Leighton Wallace | ||
@@ -56,0 +59,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
106260
665
72
14
1