/* DokuWiki MoaiEditor Matcher.js file Version : 0.5 (May 5, 2026) Author : MoaiTools License : GPL 3 (http://www.gnu.org/licenses/gpl.html) */ MoaiEditor.Matcher = class { constructor(outer) { this.matches = outer; // Variables this.lines = []; // array of plain text lines this.sections = []; // array of {prevHeaderIdx, nextHeaderIdx, nodes:[ {i, type, handle, [textContent, innerText ]} // Objects this.tools = new MoaiEditor.MatchTools(this); this.headers = new MoaiEditor.MatchHeaders(this); this.section = new MoaiEditor.MatchSection(this); } // ──────────────────────────────────── onAjax(top, bottom, startline, endline) { const start = Date.now(); // Get the lines of text to be parsed this.lines = moaiEditor.editor.current.watcher.lines.slice(startline, endline); // Parse html this.parseDOM(top, bottom); // Parse headers this.headers.parseText(this.lines); this.headers.findAll(); // Parse sections for (let section of this.sections) this.section.match(section); const elapsed = Date.now() - start; } // ──────────────────────────────────── parseDOM(top, bottom) { // Reset the arrays this.sections = []; this.headers.nodes = []; // Preparations var nodes = []; var prevHeaderIdx = null; var nodeidx = 0; var headeridx = 0; var started = false; if (top === null) started = true; // Iterate the preview nodes for (let node of document.querySelectorAll("#moaied__preview_content *")) { // Ignore nodes before and after the last preview zone if (top !== null && top.handle == node) started = true; if (!started) continue; if (bottom !== null && bottom.handle == node) break; // If we find a header if (['H1','H2','H3','H4','H5'].includes(node.tagName)) { // Store the header this.headers.nodes.push({ i : headeridx, type : node.tagName, text : node.textContent, handle : node, matchline : null }); // Create a new section if there is at least one element inside if (nodes.length > 0) { this.sections.push({ prevHeaderIdx : prevHeaderIdx, nextHeaderIdx : headeridx, nodes : nodes }); nodeidx = 0; nodes = []; } // Make header clickable moaiEditor.scroll.makeElementClickable(node); // Continue prevHeaderIdx = headeridx; headeridx += 1; continue; } // Else add the node to the current section nodes.push({ i : nodeidx, type : node.tagName, handle : node, }); nodeidx += 1; } // Create the last section if there are elements inside if (nodes.length > 0) this.sections.push({ prevHeaderIdx : prevHeaderIdx, nextHeaderIdx : null, nodes : nodes }); // /* */ } }; // End Class // ▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆ // ▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆ MoaiEditor.MatchSection = class { constructor(outer) { // Arguments this.outer = outer; this.tools = outer.tools; this.paragraphs = new MoaiEditor.MatchParagraphs(this); } // ──────────────────────────────────── match (section) { // Init variables this.text = ''; // Plaintext of this section this.lines = []; // Text lines of this section {text, [cleantext, matched, media, } this.blocks = []; // Blocks of text of this section {i, text, start, end, [cleantext, matched, media, } this.section = section; // {prevHeaderIdx, nextHeaderIdx, nodes:[]} this.startline = null; // Start line of this section in the main text // Get the text for this section (this.text, this.lines, this.blocks) this.getSectionText(); for (let type of ['P']) if (type == 'P') this.paragraphs.match(); } // ──────────────────────────────────── getSectionText () { // Calc starting and ending lines var prevLine = this.outer.headers.nodes[this.section.prevHeaderIdx]?.matchline; var nextLine = this.outer.headers.nodes[this.section.nextHeaderIdx]?.matchline; if (prevLine === undefined) prevLine = -1; if (nextLine === undefined) nextLine = this.outer.lines.length; const start = prevLine + 1; const end = nextLine - 1; this.startline = start; // Iterate the lines var blockStart = 0; var block = {text:'', start:0}; var j = 0; var b = 0; for (let i = start; i <= end; i++) { const line = this.outer.lines[i]; this.lines.push({text:line}); this.text += line + '\n'; // If line is not empty, add line to the block if (line.trim().length > 0) { block.text += line + '\n'; // If line is empty } else { // If the block is not empty, save it and start a new one if (block.text.trim().length > 0) { block.i = b; block.end = j-1; block.text = block.text.trimEnd(); this.blocks.push(block); b += 1 } // Restart the block regardless block = {text:'', start:j+1}; } j += 1; } if (block.text.trim().length > 0) { block.i = b; block.end = j-1; block.text = block.text.trimEnd(); this.blocks.push(block); } } }; // End Class // ▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆ // ▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆ MoaiEditor.MatchPreformatted = class { constructor(outer) { this.outer = outer; this.tools = outer.tools; } // ──────────────────────────────────── match () { } }; // End Class