1/*  DokuWiki MoaiEditor Matcher.js file
2    Version : 0.5 (May 5, 2026)
3    Author  : MoaiTools <info@moaitools.org>
4    License : GPL 3 (http://www.gnu.org/licenses/gpl.html) */
5
6MoaiEditor.Matcher = class {
7
8    constructor(outer) {
9
10        this.matches = outer;
11
12        // Variables
13        this.lines = [];         // array of plain text lines
14        this.sections = [];      // array of {prevHeaderIdx, nextHeaderIdx, nodes:[ {i, type, handle, [textContent, innerText ]}
15
16        // Objects
17        this.tools = new MoaiEditor.MatchTools(this);
18        this.headers = new MoaiEditor.MatchHeaders(this);
19        this.section = new MoaiEditor.MatchSection(this);
20    }
21    // ────────────────────────────────────
22    onAjax(top, bottom, startline, endline) {
23
24        const start = Date.now();
25
26        // Get the lines of text to be parsed
27        this.lines = moaiEditor.editor.current.watcher.lines.slice(startline, endline);
28
29        // Parse html
30        this.parseDOM(top, bottom);
31
32        // Parse headers
33        this.headers.parseText(this.lines);
34        this.headers.findAll();
35
36        // Parse sections
37        for (let section of this.sections)
38            this.section.match(section);
39
40        const elapsed = Date.now() - start;
41    }
42    // ────────────────────────────────────
43    parseDOM(top, bottom) {
44
45        // Reset the arrays
46        this.sections = [];
47        this.headers.nodes = [];
48
49        // Preparations
50        var nodes = [];
51        var prevHeaderIdx = null;
52        var nodeidx = 0;
53        var headeridx = 0;
54        var started = false;
55        if (top === null)
56            started = true;
57
58        // Iterate the preview nodes
59        for (let node of document.querySelectorAll("#moaied__preview_content *")) {
60
61            // Ignore nodes before and after the last preview zone
62            if (top !== null  &&  top.handle == node)
63                started = true;
64            if (!started)
65                continue;
66            if (bottom !== null  &&  bottom.handle == node)
67                break;
68
69            // If we find a header
70            if (['H1','H2','H3','H4','H5'].includes(node.tagName)) {
71
72                // Store the header
73                this.headers.nodes.push({
74                    i         : headeridx,
75                    type      : node.tagName,
76                    text      : node.textContent,
77                    handle    : node,
78                    matchline : null
79                });
80                // Create a new section if there is at least one element inside
81                if (nodes.length > 0) {
82                    this.sections.push({
83                        prevHeaderIdx : prevHeaderIdx,
84                        nextHeaderIdx : headeridx,
85                        nodes      : nodes
86                    });
87                    nodeidx = 0;
88                    nodes = [];
89                }
90                // Make header clickable
91                moaiEditor.scroll.makeElementClickable(node);
92
93                // Continue
94                prevHeaderIdx = headeridx;
95                headeridx += 1;
96                continue;
97            }
98            // Else add the node to the current section
99            nodes.push({
100                i      : nodeidx,
101                type   : node.tagName,
102                handle : node,
103            });
104            nodeidx += 1;
105        }
106        // Create the last section if there are elements inside
107        if (nodes.length > 0)
108            this.sections.push({
109                prevHeaderIdx : prevHeaderIdx,
110                nextHeaderIdx : null,
111                nodes      : nodes
112            });
113
114        //
115        /*
116        */
117    }
118}; // End Class
119
120// ▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆
121// ▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆
122
123MoaiEditor.MatchSection = class {
124
125    constructor(outer) {
126
127        // Arguments
128        this.outer = outer;
129        this.tools = outer.tools;
130        this.paragraphs = new MoaiEditor.MatchParagraphs(this);
131    }
132    // ────────────────────────────────────
133    match (section) {
134
135        // Init variables
136        this.text = '';             // Plaintext of this section
137        this.lines = [];            // Text lines of this section {text, [cleantext, matched, media, }
138        this.blocks = [];           // Blocks of text of this section {i, text, start, end, [cleantext, matched, media, }
139        this.section = section;     // {prevHeaderIdx, nextHeaderIdx, nodes:[]}
140        this.startline = null;      // Start line of this section in the main text
141
142        // Get the text for this section (this.text, this.lines, this.blocks)
143        this.getSectionText();
144
145
146        for (let type of ['P'])
147            if (type == 'P')
148                this.paragraphs.match();
149
150    }
151    // ────────────────────────────────────
152    getSectionText () {
153
154        // Calc starting and ending lines
155        var prevLine = this.outer.headers.nodes[this.section.prevHeaderIdx]?.matchline;
156        var nextLine = this.outer.headers.nodes[this.section.nextHeaderIdx]?.matchline;
157        if (prevLine === undefined) prevLine = -1;
158        if (nextLine === undefined) nextLine = this.outer.lines.length;
159        const start = prevLine + 1;
160        const end = nextLine - 1;
161        this.startline = start;
162
163        // Iterate the lines
164        var blockStart = 0;
165        var block = {text:'', start:0};
166        var j = 0;
167        var b = 0;
168        for (let i = start; i <= end; i++) {
169            const line = this.outer.lines[i];
170            this.lines.push({text:line});
171            this.text += line + '\n';
172            // If line is not empty, add line to the block
173            if (line.trim().length > 0) {
174                block.text += line + '\n';
175            // If line is empty
176            } else {
177                // If the block is not empty, save it and start a new one
178                if (block.text.trim().length > 0) {
179                    block.i = b;
180                    block.end = j-1;
181                    block.text = block.text.trimEnd();
182                    this.blocks.push(block);
183                    b += 1
184                }
185                // Restart the block regardless
186                block = {text:'', start:j+1};
187            }
188            j += 1;
189        }
190        if (block.text.trim().length > 0) {
191            block.i = b;
192            block.end = j-1;
193            block.text = block.text.trimEnd();
194            this.blocks.push(block);
195        }
196    }
197}; // End Class
198
199// ▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆
200// ▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆
201
202
203MoaiEditor.MatchPreformatted = class {
204
205    constructor(outer) {
206        this.outer = outer;
207        this.tools = outer.tools;
208    }
209    // ────────────────────────────────────
210    match () {
211
212    }
213}; // End Class
214