258 lines
9.5 KiB
JavaScript
258 lines
9.5 KiB
JavaScript
// Integration for converting pasted spreadsheet tables to Markdown tables in EasyMDE/CodeMirror
|
|
// Handles both HTML tables (from Google Sheets) and tab-separated values
|
|
|
|
window.markdownTablePaste = {
|
|
findEditor: function(editorId) {
|
|
let textarea = null;
|
|
let codeMirror = null;
|
|
|
|
if (editorId) {
|
|
const editorElement = document.getElementById(editorId);
|
|
if (editorElement) {
|
|
textarea = editorElement.querySelector('textarea');
|
|
const cmEl = editorElement.querySelector('.CodeMirror');
|
|
if (cmEl && cmEl.CodeMirror) {
|
|
codeMirror = cmEl.CodeMirror;
|
|
}
|
|
}
|
|
} else {
|
|
const containers = document.querySelectorAll('.EasyMDEContainer');
|
|
if (containers.length > 0) {
|
|
const lastContainer = containers[containers.length - 1];
|
|
textarea = lastContainer.querySelector('textarea');
|
|
const cmEl = lastContainer.querySelector('.CodeMirror');
|
|
if (cmEl && cmEl.CodeMirror) {
|
|
codeMirror = cmEl.CodeMirror;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!codeMirror && textarea && window.EasyMDE) {
|
|
const easyMDEInstances = window.EasyMDE.instances || [];
|
|
for (let i = 0; i < easyMDEInstances.length; i++) {
|
|
const instance = easyMDEInstances[i];
|
|
if (instance && instance.codemirror) {
|
|
const cmTextarea = instance.codemirror.getTextArea();
|
|
if (cmTextarea === textarea) {
|
|
codeMirror = instance.codemirror;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!codeMirror && textarea.parentElement) {
|
|
const parent = textarea.parentElement;
|
|
if (parent._easyMDEInstance && parent._easyMDEInstance.codemirror) {
|
|
codeMirror = parent._easyMDEInstance.codemirror;
|
|
}
|
|
}
|
|
}
|
|
|
|
return { textarea, codeMirror };
|
|
},
|
|
|
|
getValue: function(editorId) {
|
|
const found = this.findEditor(editorId);
|
|
if (found.codeMirror) {
|
|
return found.codeMirror.getValue();
|
|
}
|
|
if (found.textarea) {
|
|
return found.textarea.value;
|
|
}
|
|
return null;
|
|
},
|
|
|
|
setValue: function(editorId, text) {
|
|
const found = this.findEditor(editorId);
|
|
if (found.codeMirror) {
|
|
found.codeMirror.setValue(text ?? '');
|
|
return true;
|
|
}
|
|
if (found.textarea) {
|
|
found.textarea.value = text ?? '';
|
|
found.textarea.dispatchEvent(new Event('input', { bubbles: true }));
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
|
|
insertAtCursor: function(editorId, text) {
|
|
const found = this.findEditor(editorId);
|
|
if (found.codeMirror) {
|
|
found.codeMirror.replaceSelection(text);
|
|
found.codeMirror.focus();
|
|
return true;
|
|
}
|
|
if (found.textarea) {
|
|
const start = found.textarea.selectionStart || 0;
|
|
const end = found.textarea.selectionEnd || 0;
|
|
const before = found.textarea.value.substring(0, start);
|
|
const after = found.textarea.value.substring(end);
|
|
found.textarea.value = before + text + after;
|
|
found.textarea.selectionStart = found.textarea.selectionEnd = before.length + text.length;
|
|
found.textarea.dispatchEvent(new Event('input', { bubbles: true }));
|
|
found.textarea.focus();
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
|
|
/**
|
|
* Initializes paste handler for a MarkdownEditor instance.
|
|
* Finds the textarea or CodeMirror instance created by EasyMDE and attaches paste event handler.
|
|
* @param {string} editorId - The ID of the editor wrapper element, or null to find by class
|
|
*/
|
|
initialize: function(editorId) {
|
|
let attempts = 0;
|
|
const maxAttempts = 5;
|
|
|
|
const tryInitialize = function() {
|
|
attempts++;
|
|
const found = window.markdownTablePaste.findEditor(editorId);
|
|
const textarea = found.textarea;
|
|
const codeMirror = found.codeMirror;
|
|
|
|
if (textarea || codeMirror) {
|
|
const target = codeMirror || textarea;
|
|
|
|
if (target) {
|
|
// Add paste handler
|
|
if (codeMirror) {
|
|
codeMirror.on('paste', function(cm, event) {
|
|
handleManualPaste(event, cm);
|
|
});
|
|
} else if (textarea) {
|
|
textarea.addEventListener('paste', function(event) {
|
|
handleManualPaste(event, textarea);
|
|
}, true); // Use capture phase to intercept early
|
|
}
|
|
return; // Success - we're done
|
|
}
|
|
}
|
|
|
|
// If we didn't find the textarea, retry
|
|
if (attempts < maxAttempts) {
|
|
setTimeout(tryInitialize, attempts * 100);
|
|
}
|
|
};
|
|
|
|
// Start with initial delay to allow EasyMDE to initialize
|
|
setTimeout(tryInitialize, 100);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Handler for converting pasted spreadsheet tables to Markdown tables.
|
|
* Handles both HTML tables (from Google Sheets) and tab-separated values.
|
|
*/
|
|
function handleManualPaste(event, target) {
|
|
const clipboardData = event.clipboardData || window.clipboardData;
|
|
if (!clipboardData) return;
|
|
|
|
const html = clipboardData.getData('text/html');
|
|
const text = clipboardData.getData('text/plain');
|
|
|
|
let markdownTable = null;
|
|
|
|
// Check if HTML contains a table
|
|
if (html && html.includes('<table') && html.includes('</table>')) {
|
|
markdownTable = convertHtmlTableToMarkdown(html);
|
|
}
|
|
// Check if it's tab-separated text (spreadsheet cells)
|
|
else if (text && text.includes('\t') && text.includes('\n')) {
|
|
const rows = text.trim().split(/\r?\n/).filter(row => row.trim().length > 0);
|
|
if (rows.length < 1) return;
|
|
|
|
const cells = rows.map(row => row.split('\t').map(cell => cell.trim()));
|
|
const maxCols = Math.max(...cells.map(row => row.length));
|
|
|
|
// Pad rows to have same number of columns
|
|
const paddedCells = cells.map(row => {
|
|
while (row.length < maxCols) row.push('');
|
|
return row;
|
|
});
|
|
|
|
// Escape pipe characters in cells
|
|
const escapeCell = (cell) => cell.replace(/\|/g, '\\|');
|
|
|
|
// Build Markdown table
|
|
const header = paddedCells[0];
|
|
const body = paddedCells.slice(1);
|
|
|
|
const headerRow = '| ' + header.map(escapeCell).join(' | ') + ' |';
|
|
const separatorRow = '| ' + header.map(() => '---').join(' | ') + ' |';
|
|
const bodyRows = body.map(row => '| ' + row.map(escapeCell).join(' | ') + ' |');
|
|
|
|
markdownTable = [headerRow, separatorRow, ...bodyRows].join('\n') + '\n';
|
|
}
|
|
|
|
// If we have a table to insert, do it
|
|
if (markdownTable) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
|
|
// Insert into editor
|
|
if (target.getDoc && target.getDoc().replaceRange) {
|
|
// CodeMirror
|
|
const doc = target.getDoc();
|
|
const cursor = doc.getCursor();
|
|
doc.replaceRange(markdownTable, cursor);
|
|
} else if (target.setRangeText) {
|
|
// Textarea with setRangeText
|
|
const start = target.selectionStart;
|
|
const end = target.selectionEnd;
|
|
target.setRangeText(markdownTable, start, end, 'end');
|
|
target.dispatchEvent(new Event('input', { bubbles: true }));
|
|
} else {
|
|
// Fallback: insert at cursor
|
|
const start = target.selectionStart || 0;
|
|
const end = target.selectionEnd || 0;
|
|
const before = target.value.substring(0, start);
|
|
const after = target.value.substring(end);
|
|
target.value = before + markdownTable + after;
|
|
target.selectionStart = target.selectionEnd = before.length + markdownTable.length;
|
|
target.dispatchEvent(new Event('input', { bubbles: true }));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Converts an HTML table to Markdown table format
|
|
*/
|
|
function convertHtmlTableToMarkdown(html) {
|
|
const tempDiv = document.createElement('div');
|
|
tempDiv.innerHTML = html;
|
|
|
|
const table = tempDiv.querySelector('table');
|
|
if (!table) return null;
|
|
|
|
const rows = table.querySelectorAll('tr');
|
|
if (rows.length === 0) return null;
|
|
|
|
const escapeCell = (cell) => {
|
|
const text = cell.textContent || cell.innerText || '';
|
|
return text.trim().replace(/\|/g, '\\|').replace(/\n/g, ' ');
|
|
};
|
|
|
|
const markdownRows = [];
|
|
|
|
// Process header row (first row)
|
|
const headerRow = rows[0];
|
|
const headerCells = headerRow.querySelectorAll('th, td');
|
|
const headerText = Array.from(headerCells).map(escapeCell);
|
|
markdownRows.push('| ' + headerText.join(' | ') + ' |');
|
|
|
|
// Add separator
|
|
markdownRows.push('| ' + headerText.map(() => '---').join(' | ') + ' |');
|
|
|
|
// Process data rows
|
|
for (let i = 1; i < rows.length; i++) {
|
|
const row = rows[i];
|
|
const cells = row.querySelectorAll('td, th');
|
|
const cellText = Array.from(cells).map(escapeCell);
|
|
markdownRows.push('| ' + cellText.join(' | ') + ' |');
|
|
}
|
|
|
|
return markdownRows.join('\n') + '\n';
|
|
}
|