diff --git a/lib/ExpensiMark.js b/lib/ExpensiMark.js index 6b198e2b..543afa72 100644 --- a/lib/ExpensiMark.js +++ b/lib/ExpensiMark.js @@ -36,6 +36,11 @@ export default class ExpensiMark { const group = textWithinFences.replace(/(?:(?![\n\r])\s)/g, ' '); return `
${group}
`; }, + rawInputReplacement: (match, __, textWithinFences) => { + const withinFences = match.replace(/(?:```)([\s\S]*?)(?:```)/g, '$1'); + const group = textWithinFences.replace(/(?:(?![\n\r])\s)/g, ' '); + return `
${group}
`; + } }, /** @@ -59,11 +64,11 @@ export default class ExpensiMark { */ { name: 'email', - process: (textToProcess, replacement) => { + process: (textToProcess, replacement, shouldKeepRawInput) => { const regex = new RegExp( `(?!\\[\\s*\\])\\[([^[\\]]*)]\\((mailto:)?${CONST.REG_EXP.MARKDOWN_EMAIL}\\)`, 'gim' ); - return this.modifyTextForEmailLinks(regex, textToProcess, replacement); + return this.modifyTextForEmailLinks(regex, textToProcess, replacement, shouldKeepRawInput); }, replacement: (match, g1, g2) => { if (g1.match(CONST.REG_EXP.EMOJIS) || !g1.trim()) { @@ -74,12 +79,21 @@ export default class ExpensiMark { const formattedLabel = label === href ? g2 : label; return `${formattedLabel}`; }, + rawInputReplacement: (match, g1, g2, g3) => { + if (g1.match(CONST.REG_EXP.EMOJIS) || !g1.trim()) { + return match; + } + + const dataRawHref = g2 ? g2 + g3 : g3; + const href = `mailto:${g3}`; + return `${g1}`; + } }, { name: 'heading1', - process: (textToProcess, replacement, shouldKeepWhitespace = false) => { - const regexp = shouldKeepWhitespace ? /^# ( *(?! )(?:(?!
|\n|\r\n).)+)/gm : /^# +(?! )((?:(?!
|\n|\r\n).)+)/gm;
+                process: (textToProcess, replacement, shouldKeepRawInput = false) => {
+                    const regexp = shouldKeepRawInput ? /^# ( *(?! )(?:(?!
|\n|\r\n).)+)/gm : /^# +(?! )((?:(?!
|\n|\r\n).)+)/gm;
                     return textToProcess.replace(regexp, replacement);
                 },
                 replacement: '

$1

', @@ -100,6 +114,12 @@ export default class ExpensiMark { } return `${g1.trim()}`; }, + rawInputReplacement: (match, g1, g2) => { + if (g1.match(CONST.REG_EXP.EMOJIS) || !g1.trim()) { + return match; + } + return `${g1.trim()}`; + } }, /** @@ -162,6 +182,10 @@ export default class ExpensiMark { const href = Str.sanitizeURL(g2); return `${g1}${g2}${g1}`; }, + rawInputReplacement: (_match, g1, g2) => { + const href = Str.sanitizeURL(g2); + return `${g1}${g2}${g1}`; + } }, { @@ -170,16 +194,16 @@ export default class ExpensiMark { // We also want to capture a blank line before or after the quote so that we do not add extra spaces. // block quotes naturally appear on their own line. Blockquotes should not appear in code fences or // inline code blocks. A single prepending space should be stripped if it exists - process: (textToProcess, replacement, shouldKeepWhitespace = false) => { + process: (textToProcess, replacement, shouldKeepRawInput = false) => { const regex = new RegExp( /^> *(?! )(?![^<]*(?:<\/pre>|<\/code>))([^\v\n\r]+)/gm, ); - if (shouldKeepWhitespace) { - return textToProcess.replace(regex, g1 => replacement(g1, shouldKeepWhitespace)); + if (shouldKeepRawInput) { + return textToProcess.replace(regex, g1 => replacement(g1, shouldKeepRawInput)); } return this.modifyTextForQuote(regex, textToProcess, replacement); }, - replacement: (g1, shouldKeepWhitespace = false) => { + replacement: (g1, shouldKeepRawInput = false) => { // We want to enable 2 options of nested heading inside the blockquote: "># heading" and "> # heading". // To do this we need to parse body of the quote without first space let isStartingWithSpace = false; @@ -187,7 +211,7 @@ export default class ExpensiMark { isStartingWithSpace = !!g2; return ''; }); - const replacedText = this.replace(textToReplace, {filterRules: ['heading1'], shouldEscapeText: false, shouldKeepWhitespace}); + const replacedText = this.replace(textToReplace, {filterRules: ['heading1'], shouldEscapeText: false, shouldKeepRawInput}); return `
${isStartingWithSpace ? ' ' : ''}${replacedText}
`; }, }, @@ -228,6 +252,7 @@ export default class ExpensiMark { 'gim', ), replacement: '$1$2', + rawInputReplacement: '$1$2', }, { @@ -437,10 +462,10 @@ export default class ExpensiMark { * * @returns {String} */ - replace(text, {filterRules = [], shouldEscapeText = true, shouldKeepWhitespace = false} = {}) { + replace(text, {filterRules = [], shouldEscapeText = true, shouldKeepRawInput = false} = {}) { // This ensures that any html the user puts into the comment field shows as raw html let replacedText = shouldEscapeText ? _.escape(text) : text; - const excludeRules = shouldKeepWhitespace ? _.union(this.shouldKeepWhitespaceRules, filterRules) : filterRules; + const excludeRules = shouldKeepRawInput ? _.union(this.shouldKeepWhitespaceRules, filterRules) : filterRules; const rules = _.isEmpty(excludeRules) ? this.rules : _.filter(this.rules, rule => _.contains(excludeRules, rule.name)); try { @@ -449,11 +474,11 @@ export default class ExpensiMark { if (rule.pre) { replacedText = rule.pre(replacedText); } - + const replacementFunction = shouldKeepRawInput && rule.rawInputReplacement ? rule.rawInputReplacement : rule.replacement; if (rule.process) { - replacedText = rule.process(replacedText, rule.replacement, shouldKeepWhitespace); + replacedText = rule.process(replacedText, replacementFunction, shouldKeepRawInput); } else { - replacedText = replacedText.replace(rule.regex, rule.replacement); + replacedText = replacedText.replace(rule.regex, replacementFunction); } // Post-process text after applying regex @@ -585,10 +610,11 @@ export default class ExpensiMark { * @param {RegExp} regex * @param {String} textToCheck * @param {Function} replacement + * @param {Boolean} shouldKeepRawInput * * @returns {String} */ - modifyTextForEmailLinks(regex, textToCheck, replacement) { + modifyTextForEmailLinks(regex, textToCheck, replacement, shouldKeepRawInput) { let match = regex.exec(textToCheck); let replacedText = ''; let startIndex = 0; @@ -602,7 +628,10 @@ export default class ExpensiMark { filterRules: ['bold', 'strikethrough', 'italic'], shouldEscapeText: false, }); - replacedText = replacedText.concat(replacement(match[0], linkText, match[3])); + + // rawInputReplacment needs to be called with additional parameters from match + const replacedMatch = shouldKeepRawInput ? replacement(match[0], linkText, match[2], match[3]) : replacement(match[0], linkText, match[3]); + replacedText = replacedText.concat(replacedMatch); startIndex = match.index + (match[0].length); // Now we move to the next match that the js regex found in the text