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