1/* DokuWiki MoaiEditor Layout_desktop.js file 2 Version : 0.5a (May 6, 2026) 3 Author : MoaiTools <info@moaitools.org> 4 License : GPL 3 (http://www.gnu.org/licenses/gpl.html) */ 5 6/* Desktop layout class 7 ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ 8 This class handles the desktop layout. 9 10 Layout 11 ‾‾‾‾‾‾ 12 body -- 13 #moaied__wrapper -- Semitransparent overlay 14 #moaied__tplsidebar -- Container for editor (and optionally the template sidebar as well) 15 #moaied__editor -- Container for the editor itself 16 17 ┌─ #moaied__wrapper ┌─ #moaied__editor 18 │ │ 19 ┌──┴──┏━━━━━━┓┏━━━━━━━━━━━━━━━━━━┴━━━━━━┓─────┐ 20 │/ / /┃ ┃┃ ┃/ / /│ 21 │/ / /┃ ┃┃ ┃/ / /│ 22 │/ / /┃ ┃┃ ┃/ / /│ 23 │/ / /┃ ┃┃ ┃/ / /│ 24 │/ / /┃ ┃┃ ┃/ / /│ 25 │/ / /┃ ┃┃ ┃/ / /│ 26 │/ / /┃ ┃┃ ┃/ / /│ 27 │/ / /┃ ┃┃ ┃/ / /│ 28 │/ / /┃ ┃┃ ┃/ / /│ 29 │/ / /┃ ┃┃ ┃/ / /│ 30 └─────┗━━━┬━━┛┗━━━━━━━━━━━━━━━━━━━━━━━━━┛─────┘ 31 │ 32 └─ #moaied__tplsidebar (the template's sidebar) 33*/ 34MoaiEditor.LayoutDesktop = class { 35 36 constructor(layout) { 37 38 // Arguments 39 this.layout = layout; 40 41 // Settings 42 this.settings = { 43 default: { show_edit_summary:false, show_optional_buttons:false} 44 }; 45 // Variables 46 this.show_edit_summary = this.settings.default.show_edit_summary; 47 this.show_optional_buttons = this.settings.default.show_optional_buttons; 48 49 } 50 // ──────────────────────────────────── 51 deactivate () { 52 // Remove the right sidebar from the panes container 53 this.sidebar.remove(); 54 // Unhide all buttons 55 const buttons = document.querySelectorAll(".moaied-button"); 56 for (let button of buttons) 57 button.classList.remove('moaied-display-none'); 58 // Remove editor padding 59 this.layout.editor.style.paddingLeft = ''; 60 this.layout.editor.style.paddingRight = ''; 61 // Unhide edit summary 62 this.show_edit_summary = true; 63 this.setEditSummaryVisibility(); 64 } 65 // ──────────────────────────────────── 66 activate () { 67 68 // Handles 69 const editor = this.layout.editor; 70 const elements = this.layout.elements; 71 72 // Editor layout 73 this.row1 = moaiEditor.createHTML('<div id="moaied__row1"></div>'); // Page location, MoaiEditor buttons 74 this.row2 = moaiEditor.createHTML('<div id="moaied__row2"></div>'); // Toolbar buttons, edit-summary input fields 75 this.paneswrapper = moaiEditor.createHTML('<div id="moaied__panes_wrapper"></div>'); // This div allows us to scroll the panes horizontally in single pane view 76 this.footer = moaiEditor.createHTML('<div id="moaied__footer"></div>'); // Footer row 77 editor.appendChild(this.row1); 78 editor.appendChild(this.layout.msgarea); 79 editor.appendChild(this.row2); 80 editor.appendChild(this.paneswrapper); 81 editor.appendChild(this.footer); 82 editor.appendChild(this.layout.indicatorScrolling); 83 this.paneswrapper.appendChild(this.layout.panes); 84 85 // Setup Dokuwiki page-tools (currently we are not showing it but someone might want it back in the future) 86 //this.setupPageTools(); 87 88 // ──────────────── Row 1 ──────────────────── 89 90 // Left, center and right 91 this.topleft = moaiEditor.createHTML('<div id="moaied__topleft"></div>'); // Back button and document id 92 this.toc = moaiEditor.toc.container; // Table of contents 93 this.toc.classList.remove('phone'); 94 this.buttons = moaiEditor.createHTML('<div id="moaied__buttons"></div>'); // Editor buttons 95 this.row1.appendChild(this.topleft); 96 this.row1.appendChild(this.toc); 97 this.row1.appendChild(this.buttons); 98 99 // Back button 100 this.topleft.appendChild(moaiEditor.buttons.back.handle); 101 102 // Page id 103 this.topleft.appendChild(this.layout.pageid); 104 105 // Editor buttons 106 var names = [ 107 'preview', 108 'livepreview', 109 'partialpreview', 110 'autoscroll', 111 'sep', 112 'divider_moveleft', 113 'divider_moveright', 114 'panes', 115 'sep', 116 'settings', 117 'fullwidth', 118 'enabled', 119 'sep', 120 'save', 121 'cancel', 122 ]; 123 this.layout.addButtons (this.buttons, names); 124 125 // ──────────────── Row 2 ──────────────────── 126 127 // Left and right 128 this.row2left = moaiEditor.createHTML('<div id="moaied__row2_left" class="moaied-second-to-shrink"></div>'); 129 this.row2right = moaiEditor.createHTML('<div id="moaied__row2_right" class="moaied-first-to-shrink"></div>'); 130 this.row2.appendChild(this.row2left); 131 this.row2.appendChild(this.row2right); 132 133 // Default Dokuwiki toolbar 134 this.toolbar = elements.toolbar; 135 this.row2left.appendChild(this.toolbar); 136 137 138 // Edit summary 139 this.row2right.appendChild(this.layout.summary); 140 141 // ──────────────── Side ──────────────────── 142 143 // Sidebar buttons 144 this.btn_options = this.layout.createSidebarButton ('options', 'More options', this.toggleButtonsVisibility.bind(this) ); 145 this.btn_scrollhrz = this.layout.createSidebarButton ('arrowhrz', 'Toggle preview', this.toggleHorizontalScroll.bind(this) ); 146 this.btn_editsummary = this.layout.createSidebarButton ('summary', 'Edit summary', this.toggleEditSummaryVisibility.bind(this) ); 147 148 // Sidebar 149 this.sidebar = moaiEditor.createHTML('<div id="moaied__sidebar"></div>'); // Sidebar with buttons (on the right) 150 this.row2.appendChild(this.sidebar); 151 this.sidebar.appendChild(this.btn_options); 152 this.sidebar.appendChild(this.btn_scrollhrz); 153 this.sidebar.appendChild(this.btn_editsummary); 154 this.sidebar.appendChild(this.layout.btn_editor); 155 this.sidebar.appendChild(this.layout.btn_linewrap); 156 this.sidebar.appendChild(this.layout.btn_fullscreen); 157 this.sidebar.appendChild(this.layout.btn_scrolltop); 158 this.sidebar.appendChild(this.layout.btn_scrollbottom); 159 160 // Bottom right container (for logo, codemirror menu, etc) 161 this.footer.appendChild(this.layout.bottomRight); 162 163 // ──────────────── Footer ──────────────────── 164 165 // Footer 166 this.footerLeft = moaiEditor.createHTML('<div id="moaied__footer_left"></div>'); 167 this.docinfo = moaiEditor.createHTML('<div id="moaied__docinfo"> </div>'); 168 this.footer.appendChild(this.footerLeft); 169 this.footer.appendChild(this.docinfo); 170 171 // Docinfo (will not exist when creating a document) 172 const data = JSINFO.plugin_moaieditor.docinfo; 173 if (data !== null) { 174 const path = '<span>'+data.labelPath+': '+data.path+'</span>'; 175 const mod = '<span>'+data.labelMod+' '+data.time+' '+data.by+'</span>'; 176 this.docinfo.innerHTML = path + ' · ' + mod; 177 } 178 // ────────────────── Hints ───────────────────── 179 180 this.hint_scrollhrz_btn = new MoaiEditor.Hint('arrow-right', 'Click me', this.btn_scrollhrz, 40, 2); 181 this.hint_scrollhrz_bar = new MoaiEditor.Hint('arrow-corner-left-down', 'Drag the scroll bar', this.footer, 200, 52); 182 183 // ────────────── Other things ────────────────── 184 185 this.updatePanesLayout(); // Update the panes layout (side-by-side, top-bottom, single-pane) 186 this.setButtonsVisibility(); // Hide or show the optional buttons at the top 187 this.setEditSummaryVisibility(); // Hide or show the edit summary input fields 188 moaiEditor.buttons.updateDivider(); // Update the panel divider position 189 } 190 // ──────────────────────────────────── 191 onClickPanesBtn() { 192 const mode = moaiEditor.buttons.panes.mode; 193 // Show or hide hints 194 this.hint_scrollhrz_btn.disable(); 195 this.hint_scrollhrz_bar.disable(); 196 if (mode == 'single') { 197 this.hint_scrollhrz_btn.start(); 198 this.hint_scrollhrz_bar.start(); 199 } 200 // Update the layout 201 this.updatePanesLayout(); 202 } 203 // ──────────────────────────────────── 204 updatePanesLayout() { 205 206 // Handles 207 const mode = moaiEditor.buttons.panes.mode; 208 const wrapper = document.body.querySelector('#moaied__panes_wrapper'); 209 const panes = document.body.querySelector('#moaied__panes'); 210 const btn_moveleft = moaiEditor.buttons.divider_moveleft; 211 const btn_moveright = moaiEditor.buttons.divider_moveright; 212 213 // Set single or dual pane layout 214 if (wrapper) { 215 wrapper.classList.remove('moaied-single-pane'); 216 if (mode == 'single') 217 wrapper.classList.add('moaied-single-pane'); 218 } 219 // In dual pane layout, adjust the orientation of the panes (side-by-side, top-bottom) by controlling 'flex-direction' 220 if (panes) { 221 panes.classList.remove('column'); 222 if (mode == 'column') 223 panes.classList.add('column'); 224 } 225 // Update the icons of the divider adjustment buttons (to show the correct layout) 226 if (mode != 'single') { 227 btn_moveleft.mode = mode; 228 btn_moveright.mode = mode; 229 } 230 // In single pane layout 231 btn_moveleft.handle.classList.remove('moaied-display-none-secondary'); 232 btn_moveright.handle.classList.remove('moaied-display-none-secondary'); 233 this.btn_scrollhrz.classList.add('moaied-display-none'); 234 if (mode == 'single') { 235 // Hide the divider adjustment buttons (as they are not needed) 236 btn_moveleft.handle.classList.add('moaied-display-none-secondary'); 237 btn_moveright.handle.classList.add('moaied-display-none-secondary'); 238 // Show the horizontal scroll toggle button (to toggle between editor and preview panes) 239 this.btn_scrollhrz.classList.remove('moaied-display-none'); 240 } 241 // Set the maximum width depending on user settings and full-width button 242 const userSettings = JSINFO.plugin_moaieditor; 243 var maxwidth; 244 if (mode == 'row') 245 maxwidth = userSettings.editor_width_side_by_side; 246 if (mode == 'column') 247 maxwidth = userSettings.editor_width_top_bottom; 248 if (mode == 'single') 249 maxwidth = userSettings.editor_width_single_pane; 250 if (moaiEditor.buttons.fullwidth.mode == 'on') 251 maxwidth = 'none'; 252 this.layout.editor.style.maxWidth = maxwidth; 253 //console.warn(JSINFO.plugin_moaieditor); 254 } 255 // ──────────────────────────────────── 256 toggleHorizontalScroll() { 257 // Toggle between editor and preview pane in single pane layout mode (horizontal scroll) 258 const scroll = this.paneswrapper.scrollLeft; 259 const maxScroll = moaiEditor.scroll.tools.getMaxScrollX (this.paneswrapper); 260 const threshold = maxScroll / 2; 261 if (scroll > threshold) 262 this.paneswrapper.scrollLeft = 0; 263 else 264 this.paneswrapper.scrollLeft = maxScroll+10; 265 } 266 // ──────────────────────────────────── 267 toggleButtonsVisibility() { 268 // Show or hide additional buttons at the top of the editor (blue and green buttons) 269 this.show_optional_buttons = !this.show_optional_buttons; 270 this.setButtonsVisibility(); 271 } 272 setButtonsVisibility() { 273 // Hide or show the optional buttons at the top of the editor 274 const show = this.show_optional_buttons; 275 var element; 276 var names = [ 277 'livepreview', 278 'partialpreview', 279 'autoscroll', 280 'settings', 281 /*'enabled', 282 3*/ 283 ]; 284 for (let name of names) { 285 if (typeof name === 'string') 286 element = moaiEditor.buttons[name].handle; 287 else 288 element = document.getElementById('moaied__sep_'+name); 289 if (show) 290 element.classList.remove('moaied-display-none'); 291 else 292 element.classList.add('moaied-display-none'); 293 } 294 } 295 // ──────────────────────────────────── 296 toggleEditSummaryVisibility() { 297 // Show or hide the edit-summary input fields (because many users don't use this feature) 298 this.show_edit_summary = !this.show_edit_summary; 299 this.setEditSummaryVisibility(); 300 } 301 setEditSummaryVisibility() { 302 const summary = this.layout.summary; 303 if (this.show_edit_summary) 304 summary.classList.remove('moaied-display-none'); 305 else 306 summary.classList.add('moaied-display-none'); 307 } 308 // ──────────────────────────────────── 309 onWindowResize() { 310 this.adjustPaddings(); 311 } 312 // ──────────────────────────────────── 313 adjustPaddings() { 314 // 315 var w = window.innerWidth; 316 var h = window.innerHeight; 317 // Horizontal padding 318 var left = 10; 319 var right = 40; 320 if (w > 1200) { 321 let extra = (w-1200)/40; 322 left += extra; 323 right += extra*.65; 324 } 325 this.layout.editor.style.paddingLeft = left + 'px'; 326 this.layout.editor.style.paddingRight = right + 'px'; 327 328 } 329}; // End Class 330 331 332