一些检查失败了
automerge / automerge (push) Has been cancelled
react-native-android-build-apk / AssembleRelease (push) Has been cancelled
Build macOS M1 / Main (push) Has been cancelled
Joplin Continuous Integration / Main (macos-15-intel) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-11-vs2026-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-2025) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04-arm) (push) Has been cancelled
Joplin UI tests / Main (ubuntu-22.04) (push) Has been cancelled
Joplin UI tests / Main (windows-2025) (push) Has been cancelled
Joplin UI tests / Mobile (push) Has been cancelled
Close stale issues / ProcessStaleIssues (push) Has been cancelled
72 行
2.5 KiB
TypeScript
72 行
2.5 KiB
TypeScript
|
|
const getRootNode = (dom: Document) => {
|
|
return dom.querySelector('body > #rendered-md') ?? dom.body ?? dom.getRootNode();
|
|
};
|
|
|
|
// The renderer attaches source map info to individual list items,
|
|
// rather than the toplevel list block.
|
|
// The markup restoration logic, however, needs this information on
|
|
// the toplevel block.
|
|
const addListSourceMapInfo = (dom: Document) => {
|
|
const root = getRootNode(dom);
|
|
|
|
const lists = [
|
|
...dom.querySelectorAll('ol, ul'),
|
|
].filter(node => node.parentElement === root);
|
|
|
|
for (const list of lists) {
|
|
const firstChild = list.children.item(0);
|
|
const lastChild = list.children.item(list.children.length - 1);
|
|
if (!firstChild || !lastChild) continue;
|
|
if (!firstChild.hasAttribute('source-line') || !lastChild.hasAttribute('source-line-end')) continue;
|
|
|
|
const firstChildLine = firstChild.getAttribute('source-line');
|
|
const lastChildLine = lastChild.getAttribute('source-line-end');
|
|
list.classList.add('maps-to-line');
|
|
list.setAttribute('source-line', firstChildLine);
|
|
list.setAttribute('source-line-end', lastChildLine);
|
|
}
|
|
};
|
|
|
|
// Uses the renderer's source-line and source-line-end attributes to
|
|
// reconstruct the original Markdown for different parts of the given
|
|
// DOM.
|
|
const addOriginalMarkdownAttributes = (dom: Document, originalMarkup: string) => {
|
|
const lines = originalMarkup.split('\n');
|
|
|
|
const root = getRootNode(dom);
|
|
const sourceMappedToplevelElements = [
|
|
...dom.querySelectorAll('.maps-to-line[source-line][source-line-end]'),
|
|
].filter(node => node.parentElement === root);
|
|
|
|
let lastEndLine = -1;
|
|
let lastElement: Element = null;
|
|
for (const elem of sourceMappedToplevelElements) {
|
|
const startLine = Number(elem.getAttribute('source-line'));
|
|
// For block elements, the end line is sometimes the start line of the next
|
|
// block (which should be excluded from the current).
|
|
const endLine = Number(elem.getAttribute('source-line-end')) - 1;
|
|
|
|
// If the source map of two adjacent nodes overlaps, we can't know which
|
|
// part of which source line corresponds to the element.
|
|
if (startLine <= lastEndLine) {
|
|
lastElement?.removeAttribute('data-original-markup');
|
|
lastElement = null;
|
|
continue;
|
|
}
|
|
|
|
const joinedLines = lines.slice(startLine, endLine + 1).join('\n');
|
|
elem.setAttribute('data-original-markup', joinedLines);
|
|
|
|
lastEndLine = endLine;
|
|
lastElement = elem;
|
|
}
|
|
};
|
|
|
|
const preprocessEditorInput = (dom: Document, originalMarkup: string) => {
|
|
addListSourceMapInfo(dom);
|
|
addOriginalMarkdownAttributes(dom, originalMarkup);
|
|
};
|
|
|
|
export default preprocessEditorInput;
|