Handle content wrapped in a monospace pre (#14801)

GitOrigin-RevId: 7198e56ea496b8e7496bd637419586019ce56270
This commit is contained in:
Alf Eaton 2023-09-14 09:17:43 +01:00 committed by Copybot
parent 875ea723ca
commit f7e4946056
2 changed files with 59 additions and 11 deletions

View file

@ -83,11 +83,33 @@ const removeUnwantedElements = (
}
}
const findCodeContainingElement = (documentElement: HTMLElement) => {
let result: HTMLElement | null
// a code element
result = documentElement.querySelector<HTMLElement>('code')
if (result) {
return result
}
// a pre element with "monospace" somewhere in the font family
result = documentElement.querySelector<HTMLPreElement>('pre')
if (result?.style.fontFamily.includes('monospace')) {
return result
}
return null
}
// return true if the text content of the first <code> element
// is the same as the text content of the whole document element
const onlyCode = (documentElement: HTMLElement) =>
documentElement.querySelector('code')?.textContent?.trim() ===
documentElement.textContent?.trim()
const onlyCode = (documentElement: HTMLElement) => {
const codeElement = findCodeContainingElement(documentElement)
return (
codeElement?.textContent?.trim() === documentElement.textContent?.trim()
)
}
const htmlToLaTeX = (documentElement: HTMLElement) => {
// remove style elements
@ -149,12 +171,16 @@ const specialCharacterReplacer = (
return `${prefix}\\${char}`
}
const isElementContainingCode = (element: HTMLElement) =>
element.tagName === 'CODE' ||
(element.tagName === 'PRE' && element.style.fontFamily.includes('monospace'))
const protectSpecialCharacters = (documentElement: HTMLElement) => {
const walker = document.createTreeWalker(
documentElement,
NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT,
node =>
isElementNode(node) && node.tagName === 'CODE'
isElementNode(node) && isElementContainingCode(node)
? NodeFilter.FILTER_REJECT
: NodeFilter.FILTER_ACCEPT
)
@ -593,6 +619,15 @@ const selectors = [
start: () => `\n\n\\begin{verbatim}\n`,
end: () => `\n\\end{verbatim}\n\n`,
}),
createSelector({
selector: 'pre',
match: element =>
element.style.fontFamily.includes('monospace') &&
element.firstElementChild?.nodeName !== 'CODE' &&
hasContent(element),
start: () => `\n\n\\begin{verbatim}\n`,
end: () => `\n\\end{verbatim}\n\n`,
}),
createSelector({
selector: '.ol-table-wrap',
start: () => `\n\n\\begin{table}\n\\centering\n`,

View file

@ -295,23 +295,20 @@ describe('<CodeMirrorEditor/> paste HTML in Visual mode', function () {
// TODO: assert that the "Go to page" link has been unescaped
})
it('handles a pasted code block', function () {
it('handles pasted code in pre blocks', function () {
mountEditor()
const data = 'test <pre><code>foo</code></pre> test'
const data = `test <pre><code>\\textbf{foo}</code></pre> <pre style="font-family: 'Lucida Console', monospace">\\textbf{foo}</pre> test`
const clipboardData = new DataTransfer()
clipboardData.setData('text/html', data)
cy.get('@content').trigger('paste', { clipboardData })
cy.get('@content').should('have.text', 'test foo test')
cy.get('.ol-cm-environment-verbatim').should('have.length', 5)
cy.get('.cm-line').eq(2).click()
cy.get('@content').should(
'have.text',
'test \\begin{verbatim}foo\\end{verbatim} test'
'test \\textbf{foo} \\textbf{foo} test'
)
cy.get('.ol-cm-environment-verbatim').should('have.length', 10)
})
it('handles a pasted blockquote', function () {
@ -415,6 +412,22 @@ describe('<CodeMirrorEditor/> paste HTML in Visual mode', function () {
cy.get('.ol-cm-environment-verbatim').should('have.length', 0)
})
it('use text/plain for a pre element with monospace font', function () {
mountEditor()
const clipboardData = new DataTransfer()
clipboardData.setData(
'text/html',
'<pre style="font-family:Courier,monospace">foo</pre>'
)
clipboardData.setData('text/plain', 'foo')
cy.get('@content').trigger('paste', { clipboardData })
cy.get('@content').should('have.text', 'foo')
cy.get('.ol-cm-command-verb').should('have.length', 0)
cy.get('.ol-cm-environment-verbatim').should('have.length', 0)
})
it('handles pasted text with formatting', function () {
mountEditor()