Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix markdown in alt of video markdown #769

Merged
merged 1 commit into from
Aug 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions __tests__/ExpensiMark-HTML-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2078,6 +2078,25 @@ describe('Video markdown conversion to html tag', () => {
expect(parser.replace(testString)).toBe(resultString);
});

test('with alt text containing markdown', () => {
const testString = '![# fake-heading *bold* _italic_ ~strike~ [:-)]](https://example.com/video.mp4)';
const resultString = '<video data-expensify-source="https://example.com/video.mp4" ># fake-heading *bold* _italic_ ~strike~ [:-)]</video>';
expect(parser.replace(testString)).toBe(resultString);
});

test('inline code in alt', () => {
const testString = '![`code`](https://example.com/video.mp4)';
const resultString = '<video data-expensify-source="https://example.com/video.mp4" >&#x60;code&#x60;</video>';
expect(parser.replace(testString)).toBe(resultString);
});

test('blockquote in alt', () => {
const testString = '![```test```](https://example.com/video.mp4)';
const resultString = '<video data-expensify-source="https://example.com/video.mp4" >&#x60;&#x60;&#x60;test&#x60;&#x60;&#x60;</video>';
expect(parser.replace(testString)).toBe(resultString);
});


})

describe('Image markdown conversion to html tag', () => {
Expand Down
64 changes: 32 additions & 32 deletions lib/ExpensiMark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,30 @@ export default class ExpensiMark {
},
},

/**
* Converts markdown style video to video tags e.g. ![Expensify](https://www.expensify.com/attachment.mp4)
* We need to convert before image rules since they will not try to create a image tag from an existing video URL
* Extras arg could contain the attribute cache for the video tag which is cached during the html-to-markdown conversion
*/
{
name: 'video',
regex: MARKDOWN_VIDEO_REGEX,
/**
* @param extras - The extras object
* @param videoName - The first capture group - video name
* @param videoSource - The second capture group - video URL
* @return Returns the HTML video tag
*/
replacement: (extras, _match, videoName, videoSource) => {
const extraAttrs = extras && extras.videoAttributeCache && extras.videoAttributeCache[videoSource];
return `<video data-expensify-source="${Str.sanitizeURL(videoSource)}" ${extraAttrs || ''}>${videoName ? `${videoName}` : ''}</video>`;
},
rawInputReplacement: (extras, _match, videoName, videoSource) => {
const extraAttrs = extras && extras.videoAttributeCache && extras.videoAttributeCache[videoSource];
return `<video data-expensify-source="${Str.sanitizeURL(videoSource)}" data-raw-href="${videoSource}" data-link-variant="${typeof videoName === 'string' ? 'labeled' : 'auto'}" ${extraAttrs || ''}>${videoName ? `${videoName}` : ''}</video>`;
},
},

/**
* Apply inline code-block to avoid applying any other formatting rules inside of it,
* like we do for the multi-line code-blocks
Expand All @@ -166,7 +190,7 @@ export default class ExpensiMark {
// Use the url escaped version of a backtick (`) symbol. Mobile platforms do not support lookbehinds,
// so capture the first and third group and place them in the replacement.
// but we should not replace backtick symbols if they include <pre> tags between them.
regex: /(\B|_|)&#x60;(.*?(?![&#x60;])\S.*?)&#x60;(\B|_|)(?!&#x60;|[^<]*<\/pre>)/gm,
regex: /(\B|_|)&#x60;(.*?(?![&#x60;])\S.*?)&#x60;(\B|_|)(?!&#x60;|[^<]*<\/pre>|[^<]*<\/video>)/gm,
replacement: '$1<code>$2</code>$3',
},

Expand Down Expand Up @@ -204,36 +228,12 @@ export default class ExpensiMark {
{
name: 'heading1',
process: (textToProcess, replacement, shouldKeepRawInput = false) => {
const regexp = shouldKeepRawInput ? /^# ( *(?! )(?:(?!<pre>|\n|\r\n).)+)/gm : /^# +(?! )((?:(?!<pre>|\n|\r\n).)+)/gm;
const regexp = shouldKeepRawInput ? /^# ( *(?! )(?:(?!<pre>|<video>|\n|\r\n).)+)/gm : /^# +(?! )((?:(?!<pre>|<video>|\n|\r\n).)+)/gm;
return this.replaceTextWithExtras(textToProcess, regexp, EXTRAS_DEFAULT, replacement);
},
replacement: '<h1>$1</h1>',
},

/**
* Converts markdown style video to video tags e.g. ![Expensify](https://www.expensify.com/attachment.mp4)
* We need to convert before image rules since they will not try to create a image tag from an existing video URL
* Extras arg could contain the attribute cache for the video tag which is cached during the html-to-markdown conversion
*/
{
name: 'video',
regex: MARKDOWN_VIDEO_REGEX,
/**
* @param extras - The extras object
* @param videoName - The first capture group - video name
* @param videoSource - The second capture group - video URL
* @return Returns the HTML video tag
*/
replacement: (extras, _match, videoName, videoSource) => {
const extraAttrs = extras && extras.videoAttributeCache && extras.videoAttributeCache[videoSource];
return `<video data-expensify-source="${Str.sanitizeURL(videoSource)}" ${extraAttrs || ''}>${videoName ? `${videoName}` : ''}</video>`;
},
rawInputReplacement: (extras, _match, videoName, videoSource) => {
const extraAttrs = extras && extras.videoAttributeCache && extras.videoAttributeCache[videoSource];
return `<video data-expensify-source="${Str.sanitizeURL(videoSource)}" data-raw-href="${videoSource}" data-link-variant="${typeof videoName === 'string' ? 'labeled' : 'auto'}" ${extraAttrs || ''}>${videoName ? `${videoName}` : ''}</video>`;
},
},

/**
* Converts markdown style images to image tags e.g. ![Expensify](https://www.expensify.com/attachment.png)
* We need to convert before linking rules since they will not try to create a link from an existing img
Expand Down Expand Up @@ -373,9 +373,9 @@ export default class ExpensiMark {
// 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, shouldKeepRawInput = false) => {
const regex = /^(?:&gt;)+ +(?! )(?![^<]*(?:<\/pre>|<\/code>))([^\v\n\r]+)/gm;
const regex = /^(?:&gt;)+ +(?! )(?![^<]*(?:<\/pre>|<\/code>|<\/video>))([^\v\n\r]+)/gm;
if (shouldKeepRawInput) {
const rawInputRegex = /^(?:&gt;)+ +(?! )(?![^<]*(?:<\/pre>|<\/code>))([^\v\n\r]*)/gm;
const rawInputRegex = /^(?:&gt;)+ +(?! )(?![^<]*(?:<\/pre>|<\/code>|<\/video>))([^\v\n\r]*)/gm;
return this.replaceTextWithExtras(textToProcess, rawInputRegex, EXTRAS_DEFAULT, replacement);
}
return this.modifyTextForQuote(regex, textToProcess, replacement as ReplacementFn);
Expand Down Expand Up @@ -434,9 +434,9 @@ export default class ExpensiMark {
*/
{
name: 'italic',
regex: /(<(pre|code|a|mention-user)[^>]*>(.*?)<\/\2>)|((\b_+|\b)_((?![\s_])[\s\S]*?[^\s_](?<!\s))_(?![^\W_])(?![^<]*>)(?![^<]*(<\/pre>|<\/code>|<\/a>|<\/mention-user>)))/g,
regex: /(<(pre|code|a|mention-user|video)[^>]*>(.*?)<\/\2>)|((\b_+|\b)_((?![\s_])[\s\S]*?[^\s_](?<!\s))_(?![^\W_])(?![^<]*>)(?![^<]*(<\/pre>|<\/code>|<\/a>|<\/mention-user>|<\/video>)))/g,
replacement: (_extras, match, html, tag, content, text, extraLeadingUnderscores, textWithinUnderscores) => {
// Skip any <pre>, <code>, <a>, <mention-user> tag contents
// Skip any <pre>, <code>, <a>, <mention-user>, <video> tag contents
if (html) {
return html;
}
Expand Down Expand Up @@ -470,7 +470,7 @@ export default class ExpensiMark {
// \B will match everything that \b doesn't, so it works
// for * and ~: https://www.rexegg.com/regex-boundaries.html#notb
name: 'bold',
regex: /(?<!<[^>]*)(\b_|\B)\*(?![^<]*(?:<\/pre>|<\/code>|<\/a>))((?![\s*])[\s\S]*?[^\s*](?<!\s))\*\B(?![^<]*>)(?![^<]*(<\/pre>|<\/code>|<\/a>))/g,
regex: /(?<!<[^>]*)(\b_|\B)\*(?![^<]*(?:<\/pre>|<\/code>|<\/a>|<\/video>))((?![\s*])[\s\S]*?[^\s*](?<!\s))\*\B(?![^<]*>)(?![^<]*(<\/pre>|<\/code>|<\/a>|<\/video>))/g,
replacement: (_extras, match, g1, g2) => {
if (g1.includes('_')) {
return `${g1}<strong>${g2}</strong>`;
Expand All @@ -481,7 +481,7 @@ export default class ExpensiMark {
},
{
name: 'strikethrough',
regex: /(?<!<[^>]*)\B~((?![\s~])[\s\S]*?[^\s~](?<!\s))~\B(?![^<]*>)(?![^<]*(<\/pre>|<\/code>|<\/a>))/g,
regex: /(?<!<[^>]*)\B~((?![\s~])[\s\S]*?[^\s~](?<!\s))~\B(?![^<]*>)(?![^<]*(<\/pre>|<\/code>|<\/a>|<\/video>))/g,
replacement: (_extras, match, g1) => (g1.includes('</pre>') || this.containsNonPairTag(g1) ? match : `<del>${g1}</del>`),
},
{
Expand Down
Loading