Skip to content
Snippets Groups Projects
Commit 4070d58a authored by Douwe Maan's avatar Douwe Maan
Browse files

Don't copy empty elements that were not selected on purpose as GFM

parent 36ede008
No related branches found
No related tags found
3 merge requests!14773Maxraab master patch 51809,!12073Add RC2 changes to 9-3-stable,!11520Don't copy empty elements that were not selected on purpose as GFM
Pipeline #
Loading
Loading
@@ -18,12 +18,12 @@ const gfmRules = {
},
},
TaskListFilter: {
'input[type=checkbox].task-list-item-checkbox'(el, text) {
'input[type=checkbox].task-list-item-checkbox'(el) {
return `[${el.checked ? 'x' : ' '}]`;
},
},
ReferenceFilter: {
'.tooltip'(el, text) {
'.tooltip'(el) {
return '';
},
'a.gfm:not([data-link=true])'(el, text) {
Loading
Loading
@@ -39,15 +39,15 @@ const gfmRules = {
},
},
TableOfContentsFilter: {
'ul.section-nav'(el, text) {
'ul.section-nav'(el) {
return '[[_TOC_]]';
},
},
EmojiFilter: {
'img.emoji'(el, text) {
'img.emoji'(el) {
return el.getAttribute('alt');
},
'gl-emoji'(el, text) {
'gl-emoji'(el) {
return `:${el.getAttribute('data-name')}:`;
},
},
Loading
Loading
@@ -57,13 +57,13 @@ const gfmRules = {
},
},
VideoLinkFilter: {
'.video-container'(el, text) {
'.video-container'(el) {
const videoEl = el.querySelector('video');
if (!videoEl) return false;
 
return CopyAsGFM.nodeToGFM(videoEl);
},
'video'(el, text) {
'video'(el) {
return `![${el.dataset.title}](${el.getAttribute('src')})`;
},
},
Loading
Loading
@@ -74,19 +74,19 @@ const gfmRules = {
'code.code.math[data-math-style=inline]'(el, text) {
return `$\`${text}\`$`;
},
'span.katex-display span.katex-mathml'(el, text) {
'span.katex-display span.katex-mathml'(el) {
const mathAnnotation = el.querySelector('annotation[encoding="application/x-tex"]');
if (!mathAnnotation) return false;
 
return `\`\`\`math\n${CopyAsGFM.nodeToGFM(mathAnnotation)}\n\`\`\``;
},
'span.katex-mathml'(el, text) {
'span.katex-mathml'(el) {
const mathAnnotation = el.querySelector('annotation[encoding="application/x-tex"]');
if (!mathAnnotation) return false;
 
return `$\`${CopyAsGFM.nodeToGFM(mathAnnotation)}\`$`;
},
'span.katex-html'(el, text) {
'span.katex-html'(el) {
// We don't want to include the content of this element in the copied text.
return '';
},
Loading
Loading
@@ -95,7 +95,7 @@ const gfmRules = {
},
},
SanitizationFilter: {
'a[name]:not([href]):empty'(el, text) {
'a[name]:not([href]):empty'(el) {
return el.outerHTML;
},
'dl'(el, text) {
Loading
Loading
@@ -143,7 +143,7 @@ const gfmRules = {
},
},
MarkdownFilter: {
'br'(el, text) {
'br'(el) {
// Two spaces at the end of a line are turned into a BR
return ' ';
},
Loading
Loading
@@ -162,7 +162,7 @@ const gfmRules = {
'blockquote'(el, text) {
return text.trim().split('\n').map(s => `> ${s}`.trim()).join('\n');
},
'img'(el, text) {
'img'(el) {
return `![${el.getAttribute('alt')}](${el.getAttribute('src')})`;
},
'a.anchor'(el, text) {
Loading
Loading
@@ -222,10 +222,10 @@ const gfmRules = {
'sup'(el, text) {
return `^${text}`;
},
'hr'(el, text) {
'hr'(el) {
return '-----';
},
'table'(el, text) {
'table'(el) {
const theadEl = el.querySelector('thead');
const tbodyEl = el.querySelector('tbody');
if (!theadEl || !tbodyEl) return false;
Loading
Loading
@@ -233,11 +233,11 @@ const gfmRules = {
const theadText = CopyAsGFM.nodeToGFM(theadEl);
const tbodyText = CopyAsGFM.nodeToGFM(tbodyEl);
 
return theadText + tbodyText;
return [theadText, tbodyText].join('\n');
},
'thead'(el, text) {
const cells = _.map(el.querySelectorAll('th'), (cell) => {
let chars = CopyAsGFM.nodeToGFM(cell).trim().length + 2;
let chars = CopyAsGFM.nodeToGFM(cell).length + 2;
 
let before = '';
let after = '';
Loading
Loading
@@ -262,10 +262,15 @@ const gfmRules = {
return before + middle + after;
});
 
return `${text}|${cells.join('|')}|`;
const separatorRow = `|${cells.join('|')}|`;
return [text, separatorRow].join('\n');
},
'tr'(el, text) {
const cells = _.map(el.querySelectorAll('td, th'), cell => CopyAsGFM.nodeToGFM(cell).trim());
'tr'(el) {
const cellEls = el.querySelectorAll('td, th');
if (cellEls.length === 0) return false;
const cells = _.map(cellEls, cell => CopyAsGFM.nodeToGFM(cell));
return `| ${cells.join(' | ')} |`;
},
},
Loading
Loading
@@ -343,7 +348,9 @@ class CopyAsGFM {
return codeEl;
}
 
static nodeToGFM(node) {
static nodeToGFM(node, respectWhitespaceParam = false) {
let respectWhitespace = respectWhitespaceParam;
if (node.nodeType === Node.COMMENT_NODE) {
return '';
}
Loading
Loading
@@ -352,7 +359,9 @@ class CopyAsGFM {
return node.textContent;
}
 
const text = this.innerGFM(node);
respectWhitespace = respectWhitespace || (node.nodeName === 'PRE' || node.nodeName === 'CODE');
const text = this.innerGFM(node, respectWhitespace);
 
if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
return text;
Loading
Loading
@@ -366,7 +375,17 @@ class CopyAsGFM {
 
if (!window.gl.utils.nodeMatchesSelector(node, selector)) continue;
 
const result = func(node, text);
let result;
if (func.length === 2) {
// if `func` takes 2 arguments, it depends on text.
// if there is no text, we don't need to generate GFM for this node.
if (text.length === 0) continue;
result = func(node, text);
} else {
result = func(node);
}
if (result === false) continue;
 
return result;
Loading
Loading
@@ -376,7 +395,7 @@ class CopyAsGFM {
return text;
}
 
static innerGFM(parentNode) {
static innerGFM(parentNode, respectWhitespace = false) {
const nodes = parentNode.childNodes;
 
const clonedParentNode = parentNode.cloneNode(true);
Loading
Loading
@@ -386,13 +405,19 @@ class CopyAsGFM {
const node = nodes[i];
const clonedNode = clonedNodes[i];
 
const text = this.nodeToGFM(node);
const text = this.nodeToGFM(node, respectWhitespace);
 
// `clonedNode.replaceWith(text)` is not yet widely supported
clonedNode.parentNode.replaceChild(document.createTextNode(text), clonedNode);
}
 
return clonedParentNode.innerText || clonedParentNode.textContent;
let nodeText = clonedParentNode.innerText || clonedParentNode.textContent;
if (!respectWhitespace) {
nodeText = nodeText.trim();
}
return nodeText;
}
}
 
Loading
Loading
---
title: Don't copy empty elements that were not selected on purpose as GFM
merge_request:
author:
Loading
Loading
@@ -51,7 +51,6 @@ describe 'Copy as GFM', feature: true, js: true do
 
To see how GitLab looks please see the [features page on our website](https://about.gitlab.com/features/).
 
- Manage Git repositories with fine grained access controls that keep your code secure
 
- Perform code reviews and enhance collaboration with merge requests
Loading
Loading
@@ -352,7 +351,6 @@ describe 'Copy as GFM', feature: true, js: true do
<<-GFM.strip_heredoc,
- Nested
 
- Lists
GFM
 
Loading
Loading
@@ -375,7 +373,6 @@ describe 'Copy as GFM', feature: true, js: true do
<<-GFM.strip_heredoc,
1. Nested
 
1. Numbered lists
GFM
 
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment