/* DokuWiki MoaiEditor Layout_vphone.js file Version : 0.5 (May 5, 2026) Author : MoaiTools License : GPL 3 (http://www.gnu.org/licenses/gpl.html) */ /* Vertical phone layout class ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ This class handles the vertical phone layout. Note: Check 'style.css' to see the media queries that handle the other part of the responsive behavior. Those look like this: @media (max-width: 600px) Layout ‾‾‾‾‾‾ body -- #moaied__wrapper -- semitransparent overlay <-- scrolls horizontally to show left and right side #moaied__editor -- holds both sides #moaied__phone_left -- several options and buttons #moaied__phone_right -- toolbar, editor, preview */ MoaiEditor.LayoutPhoneVertical = class { constructor(layout) { // Arguments this.layout = layout; } // ──────────────────────────────────── deactivate () { // Placeholder } // ──────────────────────────────────── activate () { // Handles const editor = this.layout.editor; const elements = this.layout.elements; // Bind functions to buttons this.btn_goLeft = moaiEditor.buttons.goLeft; this.btn_goRight = moaiEditor.buttons.goRight; this.btn_goLeft.onClick = this.goLeft; this.btn_goRight.onClick = this.goRight; // Create the basic layout this.left = moaiEditor.createHTML('
'); // Buttons and settings this.right = moaiEditor.createHTML('
'); // Both panes (editor and preview) editor.appendChild(this.left); editor.appendChild(this.right); // Show right side initially document.getElementById('moaied__wrapper').scrollLeft = window.innerWidth; // ──────────────── Left side ──────────────────── // Rows this.leftRow1 = moaiEditor.createHTML('
'); this.leftRow2 = moaiEditor.createHTML('
'); this.left.appendChild(this.leftRow1); this.left.appendChild(this.leftRow2); // Row 1 - left side (back button and page id) var left = this.leftRow1.firstChild; left.appendChild(moaiEditor.buttons.back.handle); left.appendChild(this.layout.pageid); // Row 1 - right side (go right button) var right = this.leftRow1.lastChild; right.appendChild(this.btn_goRight.handle); // Row 2 - container for the rest of the page var main = moaiEditor.createHTML('
'); // Container for: table of contents, summary edit, document info, etc var sidebar = moaiEditor.createHTML('
'); this.leftRow2.appendChild(main); this.leftRow2.appendChild(sidebar); // Sidebar sidebar.appendChild(this.layout.btn_linewrap); sidebar.appendChild(this.layout.btn_fullscreen); sidebar.appendChild(this.layout.btn_editor); sidebar.appendChild(this.layout.btn_scrolltop); sidebar.appendChild(this.layout.btn_scrollbottom); sidebar.appendChild(this.layout.bottomRight); // Editor buttons this.leftButtons = moaiEditor.createHTML('
'); main.appendChild(this.leftButtons); var names = [ 'livepreview', 'partialpreview', 'autoscroll', 'sep', 'settings', 'enabled', ]; this.layout.addButtons(this.leftButtons, names); // Message area main.appendChild(this.layout.msgarea); // Table of contents var toc = moaiEditor.toc.container; toc.classList.add('phone'); main.appendChild(toc); // Edit summary main.appendChild(this.layout.summary); // Separator var sep = moaiEditor.createHTML('
'); main.appendChild(sep); // Docinfo (will not exist when creating a document) const data = JSINFO.plugin_moaieditor.docinfo; if (data !== null) { // Document path on disk const diskpath = moaiEditor.createHTML('
'+data.path+'
'); main.appendChild(diskpath); // Last modified const lastmodified = moaiEditor.createHTML('
'+data.time+'  '+data.by+'
'); main.appendChild(lastmodified); } // ──────────────── Right side ──────────────────── // Main elements this.rightHeaderContainer = moaiEditor.createHTML('
'); this.rightHeaderDetachable = moaiEditor.createHTML('
'); // Detachable header needed because of the weird way browsers work in iOS, which requires hacks to have a fixed header or footer in your page this.right.appendChild(this.rightHeaderContainer); this.right.appendChild(this.layout.panes); // Header this.rightToolbar = moaiEditor.createHTML('
'); this.rightButtons = moaiEditor.createHTML('
'); this.rightHeaderDetachable.appendChild(this.rightButtons); this.rightHeaderDetachable.appendChild(this.rightToolbar); this.rightHeaderContainer.appendChild(this.rightHeaderDetachable); // Editor buttons row (Preview, Save, Cancel, etc) var left = this.rightButtons.firstChild; var right = this.rightButtons.lastChild; left.appendChild(this.btn_goLeft.handle); right.appendChild(moaiEditor.buttons.preview.handle); right.appendChild(moaiEditor.buttons.save.handle); right.appendChild(moaiEditor.buttons.cancel.handle); // Default Dokuwiki toolbar row this.toolbar = elements.toolbar; this.rightToolbar.prepend(this.toolbar); // Handle the weird behavior of iOS browsers by having a detachable header which does not dissapear whenever the on-screen-keyboard is opened this.height = { rightToolbar: this.rightToolbar.getBoundingClientRect().height, rightButtons: this.rightButtons.getBoundingClientRect().height, }; window.visualViewport.addEventListener('scroll', this.onVisualViewportChange.bind(this)); window.visualViewport.addEventListener('resize', this.onVisualViewportChange.bind(this)); // ─────────────── Cosmetic things ───────────────── this.btn_goRight.handle.style.height = this.btn_goLeft.handle.getBoundingClientRect().height + 'px'; // ─────────────────── Debug ─────────────────────── // Debug line /* this.debugline = moaiEditor.createHTML('
'); this.rightToolbar.prepend(this.debugline); var interval = setInterval(this.onDebugInterval.bind(this), 200); */ // ───────────── Swipes and scroll ───────────────── /* document.querySelector("#moaied__phone_right").addEventListener('touchmove', event => { event.preventDefault(); }); */ // Detect swipes new MoaiEditor.Swipe(this.left, this.onSwipe.bind(this), 120); new MoaiEditor.Swipe(this.rightButtons, this.onSwipe.bind(this), 120); // Show a little bit of the preview (to let the user know there is a preview available) this.layout.panes.scrollLeft = window.innerWidth/5; } // ──────────────────────────────────── onWindowResize() { // Placeholder } // ──────────────────────────────────── onVisualViewportChange() { // Handles const container = this.rightHeaderContainer; const detachable = this.rightHeaderDetachable; // Detach the header and position it into view if the browser has iOS behavior if (window.visualViewport.pageTop > 3 && window.visualViewport.scale < 1.3) { detachable.classList.add('moaied-header-detached'); detachable.style.top = window.visualViewport.pageTop+'px'; } else detachable.classList.remove('moaied-header-detached'); // Hide the editor buttons if the vertical space is small (due to on-screen-keyboard opened on a small phone) if (window.visualViewport.height < 380) this.rightButtons.style.display = 'none'; else this.rightButtons.style.display = 'flex'; } // ──────────────────────────────────── onSwipe(direction) { if (direction == 'left') this.goRight(); if (direction == 'right') this.goLeft(); } goLeft() { document.getElementById('moaied__wrapper').scrollLeft = 0; } goRight() { document.getElementById('moaied__wrapper').scrollLeft = window.innerWidth; } // ──────────────────────────────────── onDebugInterval() { const html = document.documentElement; const body = document.body; const htmlHeight = html.getBoundingClientRect().height; const bodyHeight = body.getBoundingClientRect().height; this.debugline.textContent = "innerHeight: "+window.innerHeight+" - Scroll: "+html.scrollTop+" - htmlH: "+htmlHeight+" - bodyH: "+bodyHeight; } }; // End Class