fix(#4945): keep native copy for cell sub-selections
This commit is contained in:
1 parent
0256b98a3e
commit
8c09c8dfde
2 files changed
+116
-11
No files matched your search
+27
-3
@@ -486,11 +486,17 @@ function _markdownTableCopyPayloadForTable(table){
|
||||
if(!table||!table.rows) return null;
|
||||
const rows=Array.from(table.rows||[]);
|
||||
if(!rows.length) return null;
|
||||
let headerRowCount=0;
|
||||
while(headerRowCount<rows.length){
|
||||
const cells=Array.from(rows[headerRowCount].cells||[]);
|
||||
if(!cells.length||!cells.every((cell)=>cell&&cell.tagName==='TH')) break;
|
||||
headerRowCount++;
|
||||
}
|
||||
|
||||
const htmlRows=rows.map((row)=>{
|
||||
const renderRows=(rowSet)=>rowSet.map((row)=>{
|
||||
const cellTag=(cell)=>String(cell&&cell.tagName?cell.tagName.toLowerCase():'td');
|
||||
const cells=Array.from(row.cells||[])
|
||||
.filter((cell)=>cell&&cell.nodeType===1 || cell&&cell.nodeType===3)
|
||||
.filter((cell)=>cell&&cell.nodeType===1)
|
||||
.map((cell)=>{
|
||||
const tag=cellTag(cell);
|
||||
const text=_sanitizeMarkdownTableCellText(cell);
|
||||
@@ -499,6 +505,12 @@ function _markdownTableCopyPayloadForTable(table){
|
||||
.join('');
|
||||
return `<tr>${cells}</tr>`;
|
||||
}).join('');
|
||||
const headerRows=headerRowCount?renderRows(rows.slice(0, headerRowCount)):'';
|
||||
const bodyRows=renderRows(rows.slice(headerRowCount));
|
||||
const tableSections=[
|
||||
headerRows?`<thead>${headerRows}</thead>`:'',
|
||||
bodyRows?`<tbody>${bodyRows}</tbody>`:'',
|
||||
].join('');
|
||||
|
||||
const plainRows=rows.map((row)=>{
|
||||
return Array.from(row.cells||[])
|
||||
@@ -506,7 +518,7 @@ function _markdownTableCopyPayloadForTable(table){
|
||||
.join('\t');
|
||||
}).join('\n');
|
||||
|
||||
return {html:`<table>${htmlRows}</table>`, plain:plainRows};
|
||||
return {html:`<table>${tableSections}</table>`, plain:plainRows};
|
||||
}
|
||||
|
||||
function _findEnhancedMarkdownTable(node){
|
||||
@@ -518,6 +530,15 @@ function _findEnhancedMarkdownTable(node){
|
||||
return null;
|
||||
}
|
||||
|
||||
function _findMarkdownTableCell(node){
|
||||
let current=node&&node.nodeType===3?node.parentElement:node;
|
||||
while(current){
|
||||
if(current.matches&¤t.matches('th,td')) return current;
|
||||
current=current.parentElement||current.parentNode;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function _findEnhancedMarkdownTableFromRange(range){
|
||||
if(!range) return null;
|
||||
const found=_findEnhancedMarkdownTable(range.startContainer)
|
||||
@@ -543,6 +564,9 @@ function _handleMarkdownTableCopy(event){
|
||||
if(!selection||selection.isCollapsed||!selection.rangeCount) return;
|
||||
const range=selection.getRangeAt(0);
|
||||
if(!range) return;
|
||||
const startCell=_findMarkdownTableCell(range.startContainer);
|
||||
const endCell=_findMarkdownTableCell(range.endContainer);
|
||||
if(startCell&&endCell&&startCell===endCell) return;
|
||||
const table=_findEnhancedMarkdownTableFromRange(range);
|
||||
if(!table||!table.matches||!table.matches('table[data-markdown-table-enhanced]')) return;
|
||||
const payload=_markdownTableCopyPayloadForTable(table);
|
||||
|
||||
@@ -24,15 +24,53 @@ function extractFunc(name) {
|
||||
const re = new RegExp('function\\s+' + name + '\\s*\\(');
|
||||
const start = src.search(re);
|
||||
if (start < 0) throw new Error(name + ' not found');
|
||||
let i = src.indexOf('{', start);
|
||||
let depth = 1;
|
||||
i++;
|
||||
while (depth > 0 && i < src.length) {
|
||||
if (src[i] === '{') depth++;
|
||||
else if (src[i] === '}') depth--;
|
||||
i++;
|
||||
const braceStart = src.indexOf('{', start);
|
||||
let depth = 0;
|
||||
let inString = null;
|
||||
let escaped = false;
|
||||
let inLineComment = false;
|
||||
let inBlockComment = false;
|
||||
for (let i = braceStart; i < src.length; i++) {
|
||||
const ch = src[i];
|
||||
const next = i + 1 < src.length ? src[i + 1] : '';
|
||||
if (inLineComment) {
|
||||
if (ch === '\n') inLineComment = false;
|
||||
continue;
|
||||
}
|
||||
if (inBlockComment) {
|
||||
if (ch === '*' && next === '/') {
|
||||
inBlockComment = false;
|
||||
i++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (inString) {
|
||||
if (escaped) escaped = false;
|
||||
else if (ch === '\\') escaped = true;
|
||||
else if (ch === inString) inString = null;
|
||||
continue;
|
||||
}
|
||||
if (ch === '/' && next === '/') {
|
||||
inLineComment = true;
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (ch === '/' && next === '*') {
|
||||
inBlockComment = true;
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (ch === "'" || ch === '"' || ch === '`') {
|
||||
inString = ch;
|
||||
continue;
|
||||
}
|
||||
if (ch === '{') depth++;
|
||||
else if (ch === '}') {
|
||||
depth--;
|
||||
if (depth === 0) return src.slice(start, i + 1);
|
||||
}
|
||||
}
|
||||
return src.slice(start, i);
|
||||
throw new Error(name + ' brace scan failed');
|
||||
}
|
||||
|
||||
const required = [
|
||||
@@ -41,6 +79,7 @@ const required = [
|
||||
'_markdownTableCellText',
|
||||
'_sanitizeMarkdownTableCellText',
|
||||
'_findEnhancedMarkdownTable',
|
||||
'_findMarkdownTableCell',
|
||||
'_findEnhancedMarkdownTableFromRange',
|
||||
'_markdownTableCopyPayloadForTable',
|
||||
'_handleMarkdownTableCopy',
|
||||
@@ -260,6 +299,8 @@ const payload = _markdownTableCopyPayloadForTable(table);
|
||||
console.log(JSON.stringify(payload));
|
||||
"""
|
||||
)
|
||||
assert "<thead><tr><th>Product</th><th>Price</th></tr></thead>" in out["html"]
|
||||
assert "<tbody><tr><td>Widget</td><td>12</td></tr></tbody>" in out["html"]
|
||||
assert "<th>Product</th>" in out["html"]
|
||||
assert "<th>Price</th>" in out["html"]
|
||||
assert "markdown-table-sort" not in out["html"]
|
||||
@@ -434,3 +475,43 @@ console.log(JSON.stringify({ prevented: event.preventDefaultCalled, data: clipbo
|
||||
assert out["prevented"] is True
|
||||
assert "<th>Product</th>" in out["data"]["text/html"]
|
||||
assert out["data"]["text/plain"].startswith("Product\tPrice")
|
||||
|
||||
|
||||
def test_single_cell_subselection_leaves_native_copy_unmodified():
|
||||
out = _run_js(
|
||||
"""
|
||||
const {table, body} = buildEnhancedTableFixture(true);
|
||||
|
||||
const range = {
|
||||
startContainer: body.cells[0].children[0],
|
||||
endContainer: body.cells[0].children[0],
|
||||
commonAncestorContainer: body.cells[0],
|
||||
};
|
||||
|
||||
window.getSelection = () => ({
|
||||
isCollapsed: false,
|
||||
rangeCount: 1,
|
||||
getRangeAt: () => range,
|
||||
});
|
||||
|
||||
const clipboard = {
|
||||
data: {},
|
||||
setData(type, value) {
|
||||
this.data[type] = value;
|
||||
}
|
||||
};
|
||||
|
||||
const event = {
|
||||
preventDefaultCalled: false,
|
||||
preventDefault() {
|
||||
this.preventDefaultCalled = true;
|
||||
},
|
||||
clipboardData: clipboard,
|
||||
};
|
||||
|
||||
_handleMarkdownTableCopy(event);
|
||||
console.log(JSON.stringify({ prevented: event.preventDefaultCalled, data: clipboard.data }));
|
||||
"""
|
||||
)
|
||||
assert out["prevented"] is False
|
||||
assert out["data"] == {}
|
||||
Reference in new issue
Block a user