forked from esm7/obsidian-rtl
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathAutoDirPostProcessor.ts
122 lines (105 loc) · 3.97 KB
/
AutoDirPostProcessor.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import { MarkdownPostProcessorContext } from 'obsidian';
import { Direction, detectDirection } from "globals";
let lastDetectedDir: Direction = 'ltr';
// Special nodes are which the direction style should get applied on the parent
// element, Because changing their own direction won't take effect.
const specialNodes = ['A', 'STRONG', 'EM', 'DEL', 'CODE']
/*
* This recursively breaks multi-line <p> elements into multiple DIVs, to enable the post-processor to set
* a different text direction to each such line.
*/
function breaksToDivs(el: HTMLElement) {
if (!el) return;
if (el.tagName == 'P') {
const splitText = el.innerHTML.split('<br>');
if (splitText.length > 1) {
let newInnerHtml = '';
splitText.map((line) => { newInnerHtml += `<div class="esm-split">${line}</div>`; });
el.innerHTML = newInnerHtml;
}
}
if (el.children && el.children.length > 0) {
for (let i = 0; i < el.children.length; i++)
breaksToDivs(el.children[i] as HTMLElement);
}
}
// Try to detect if the postprocessor was asked to run inside a canvas element, and in that case, use the
// supplied setPreviewDirection function to launch the plugin logic and set the text to the file's direction.
function detectCanvasElement(el: HTMLElement, ctx: MarkdownPostProcessorContext, setPreviewDirection: SetPreviewDirection) {
const container = (ctx as any).containerEl as HTMLElement;
if (container && container.closest) {
const possibleCanvas = container.closest('.canvas-node-content');
if (possibleCanvas) {
const markdownPreview = container.closest('.markdown-preview-view');
if (markdownPreview && markdownPreview instanceof HTMLDivElement) {
// Mark this canvas as RTL or LTR
setPreviewDirection(ctx.sourcePath, markdownPreview);
}
}
}
}
type SetPreviewDirection = (path: string, markdownPreviewElement: HTMLDivElement) => void;
/*
* This Markdown post-processor handles the Reading view and other rendered components of notes.
* It detects the direction for each node individually and adds corresponding CSS classes that are
* later referenced in styles.css.
*/
export const autoDirectionPostProcessor = (el: HTMLElement, ctx: MarkdownPostProcessorContext, setPreviewDirection: SetPreviewDirection) => {
let shouldAddDir = false, addedDir = false;
detectCanvasElement(el, ctx, setPreviewDirection);
// Obsidian renders adjacent lines as one <p> element with <br> breaks. Since these cannot
// be set a direction individually, the following breaks them into individual divs.
breaksToDivs(el);
for (let i = 0; i < el.childNodes.length; i++) {
const n = el.childNodes[i];
if (!addedDir && n.nodeName === '#text' && n.nodeValue && n.nodeValue !== "\n") {
const dir = detectDirection(n.nodeValue);
if (dir) {
addedDir = true;
lastDetectedDir = dir;
if (specialNodes.contains(el.nodeName) && el.parentElement) {
let target = nonSpecialParent(el.parentElement);
if (target != null) {
addDirClassIfNotAddedBefore(target, dirClass(dir));
}
} else {
addDirClassIfNotAddedBefore(el, dirClass(dir));
if (el.parentElement && el.parentElement.nodeName === 'LI') {
addDirClassIfNotAddedBefore(el.parentElement, dirClass(dir));
}
}
}
shouldAddDir = true;
continue;
}
autoDirectionPostProcessor(n as HTMLElement, ctx, setPreviewDirection);
if (i === el.childNodes.length - 1 && shouldAddDir && !addedDir) {
el.addClass(dirClass(lastDetectedDir));
}
}
if (el.nodeName === "UL") {
const lis = el.querySelectorAll('li');
if (lis.length > 0 && lis[0].hasClass('esm-rtl')) {
el.addClass(dirClass('rtl'));
}
}
}
function dirClass(dir: Direction): string {
if (dir === 'rtl') {
return 'esm-rtl';
} else {
return 'esm-ltr';
}
}
function addDirClassIfNotAddedBefore(el: HTMLElement, cls: string) {
if (el.hasClass('esm-rtl') || el.hasClass('esm-ltr')) {
return;
}
el.addClass(cls);
}
function nonSpecialParent(el: HTMLElement) {
while (specialNodes.contains(el.nodeName)) {
el = el.parentElement;
}
return el;
}