/* DokuWiki MoaiEditor Toc.js file Version : 0.5a (May 6, 2026) Author : MoaiTools License : GPL 3 (http://www.gnu.org/licenses/gpl.html) */ /* Shows a table of contents (at the top of the editor) which scrolls both panes to the chosen section. It has a header depth selector on the side to reduce the amount of headers shown in large documents. Html structure: #moaied__toc -- div #moaied__toc_title -- span #moaied__toc_dropdowns -- div #moaied__toc_depth -- select #moaied__toc_wrapper -- div #moaied__toc_sections -- select #moaied__toc_dummy -- select */ MoaiEditor.ToC = class { constructor () { // Variables this.text = ''; // Text for dummy dropdown (for illusion of main dropdown persistence) this.headers = []; // Array of {i, type, level, text, handle} // Objects this.depth = new MoaiEditor.LocalStorage('toc_depth', 5, [2,3,4,5]); // Create the elements const container = moaiEditor.createHTML(' '); const title = moaiEditor.createHTML(' '); const dropdowns = moaiEditor.createHTML('
'); const depth = moaiEditor.createHTML(''); const wrapper = moaiEditor.createHTML('
'); const sections = moaiEditor.createHTML(''); const dummy = moaiEditor.createHTML(''); // Add children container.appendChild(title); container.appendChild(dropdowns); dropdowns.appendChild(wrapper); dropdowns.appendChild(depth); wrapper.appendChild(sections); wrapper.appendChild(dummy); // Add user input event handlers sections.addEventListener("change", this.onSectionChange.bind(this)); depth.addEventListener("change", this.onDepthChange.bind(this)); // Add tooltips sections.title = "Scroll to a specific section"; depth.title = "Choose the depth of the table of contents"; // Populate the depth chooser (h2, h3, h4, h5) for (let i of [2,3,4,5]) depth.appendChild(moaiEditor.createHTML('')); // Keep some handles to the HTML elements this.container = container; this.sections = sections; this.dummy = dummy; this.depthSelector = depth; } // ──────────────────────────────────── // Called whenever matches are recalculated (typically on preview updates) update () { let i = 0; this.headers = []; // Gather all matched headers for (let match of moaiEditor.matches.matches) if (['H1','H2','H3','H4','H5'].includes(match.type)) { this.headers.push({ i : i, type : match.type, level : Number(match.type.substr(1,1)), text : match.handle.textContent, handle : match.handle }); i += 1; } // Update UI this.redraw(); } // ──────────────────────────────────── redraw () { // Update the depth dropdown selected option to what is saved const depth = this.depth.value; this.depthSelector.selectedIndex = depth-2; // Remove all previous options this.sections.options.length = 0; // If there is only one

dont show it (makes the element wider because everything else will be indented) var ignoreH1 = 0; var count = 0; for (let header of this.headers) if (header.type == 'H1') count += 1; if (count < 2) ignoreH1 = 1; // Add an empty option first this.sections.appendChild(moaiEditor.createHTML('')); // Add options for (let header of this.headers) { if (ignoreH1 == 1 && header.type == "H1") continue; if (header.level > depth) continue; const indent = " ".repeat(4*(header.level-1-ignoreH1)); const html = moaiEditor.createHTML(''); this.sections.appendChild(html); } // Hide the elements if there are no sections to show if (this.sections.options.length < 2) { this.container.style.display = 'none'; return; } // Show them otherwise this.container.style.display = 'flex'; // Set dummy width const width = this.sections.getBoundingClientRect().width; this.dummy.style.width = width+'px'; // Set dummy value this.dummy.options.length = 0; this.dummy.appendChild(moaiEditor.createHTML('')); } // ──────────────────────────────────── onDepthChange () { // Store the value const value = this.depthSelector.value; this.depth.value = parseInt(value); // Update the UI this.text = ''; this.redraw(); } // ──────────────────────────────────── onSectionChange () { // Update the text in the dummy const value = this.sections.value; this.text = ''; if (value != '') this.text = this.headers[value].text; // Update the UI this.redraw(); // Return if no section was selected if (value == '') return; // Scroll const element = this.headers[value].handle; moaiEditor.scroll.toc (element); } }; // End Class