1/* DokuWiki MoaiEditor Layout_vphone.js file 2 Author : MoaiTools <info@moaitools.org> 3 License : GPL 3 (http://www.gnu.org/licenses/gpl.html) */ 4 5/* Vertical phone layout class 6 ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ 7 This class handles the vertical phone layout. 8 9 Note: Check 'style.css' to see the media queries that handle the other part of the responsive behavior. 10 Those look like this: @media (max-width: 600px) 11 12 Layout 13 ‾‾‾‾‾‾ 14 body -- 15 #moaied__wrapper -- semitransparent overlay <-- scrolls horizontally to show left and right side 16 #moaied__editor -- holds both sides 17 #moaied__phone_left -- several options and buttons 18 #moaied__phone_right -- toolbar, editor, preview 19*/ 20MoaiEditor.LayoutPhoneVertical = class { 21 22 constructor(layout) { 23 24 // Arguments 25 this.layout = layout; 26 } 27 28 deactivate () { 29 // Placeholder 30 } 31 32 activate () { 33 34 // Handles 35 const editor = this.layout.editor; 36 const elements = this.layout.elements; 37 38 // Bind functions to buttons 39 this.btn_goLeft = moaiEditor.buttons.goLeft; 40 this.btn_goRight = moaiEditor.buttons.goRight; 41 this.btn_goLeft.onClick = this.goLeft; 42 this.btn_goRight.onClick = this.goRight; 43 44 // Create the basic layout 45 this.left = moaiEditor.createHTML('<div id="moaied__phone_left"></div>'); // Buttons and settings 46 this.right = moaiEditor.createHTML('<div id="moaied__phone_right"></div>'); // Both panes (editor and preview) 47 editor.appendChild(this.left); 48 editor.appendChild(this.right); 49 50 // Show right side initially 51 document.getElementById('moaied__wrapper').scrollLeft = window.innerWidth; 52 53 // ──────────────── Left side ──────────────────── 54 55 // Rows 56 this.leftRow1 = moaiEditor.createHTML('<div id="moaied__phone_left_row1" class="moaied-phone-row"><div id="moaied__phone_topleft"></div><div></div></div>'); 57 this.leftRow2 = moaiEditor.createHTML('<div id="moaied__phone_left_row2" class="moaied-phone-row"></div>'); 58 this.left.appendChild(this.leftRow1); 59 this.left.appendChild(this.leftRow2); 60 61 // Row 1 - left side (back button and page id) 62 var left = this.leftRow1.firstChild; 63 left.appendChild(moaiEditor.buttons.back.handle); 64 left.appendChild(this.layout.pageid); 65 66 // Row 1 - right side (go right button) 67 var right = this.leftRow1.lastChild; 68 right.appendChild(this.btn_goRight.handle); 69 70 // Row 2 - container for the rest of the page 71 var main = moaiEditor.createHTML('<div id="moaied__phone_left_main"></div>'); // Container for: table of contents, summary edit, document info, etc 72 var sidebar = moaiEditor.createHTML('<div id="moaied__phone_sidebar"></div>'); 73 this.leftRow2.appendChild(main); 74 this.leftRow2.appendChild(sidebar); 75 76 // Sidebar 77 sidebar.appendChild(this.layout.btn_linewrap); 78 sidebar.appendChild(this.layout.btn_fullscreen); 79 sidebar.appendChild(this.layout.btn_editor); 80 sidebar.appendChild(this.layout.btn_scrolltop); 81 sidebar.appendChild(this.layout.btn_scrollbottom); 82 sidebar.appendChild(this.layout.bottomRight); 83 84 // Editor buttons 85 this.leftButtons = moaiEditor.createHTML('<div id="moaied__phone_left_buttons"></div>'); 86 main.appendChild(this.leftButtons); 87 var names = [ 88 'livepreview', 89 'partialpreview', 90 'autoscroll', 91 'sep', 92 'settings', 93 'enabled', 94 ]; 95 this.layout.addButtons(this.leftButtons, names); 96 97 // Message area 98 main.appendChild(this.layout.msgarea); 99 100 // Table of contents 101 var toc = moaiEditor.toc.container; 102 toc.classList.add('phone'); 103 main.appendChild(toc); 104 105 // Edit summary 106 main.appendChild(this.layout.summary); 107 108 // Separator 109 var sep = moaiEditor.createHTML('<div id="moaied__phone_left_main_separator"></div>'); 110 main.appendChild(sep); 111 112 // Docinfo (will not exist when creating a document) 113 const data = JSINFO.plugin_moaieditor.docinfo; 114 if (data !== null) { 115 116 // Document path on disk 117 const diskpath = moaiEditor.createHTML('<div id="moaied__phone_diskpath"><label>'+data.labelPath+'</label>'+data.path+'</div>'); 118 main.appendChild(diskpath); 119 120 // Last modified 121 const lastmodified = moaiEditor.createHTML('<div id="moaied__phone_lastmodified"><label>'+data.labelMod+'</label>'+data.time+' '+data.by+'</div>'); 122 main.appendChild(lastmodified); 123 } 124 // ──────────────── Right side ──────────────────── 125 126 // Main elements 127 this.rightHeaderContainer = moaiEditor.createHTML('<div id="moaied__phone_right_header_container"></div>'); 128 this.rightHeaderDetachable = moaiEditor.createHTML('<div id="moaied__phone_right_header_detachable"></div>'); // 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 129 this.right.appendChild(this.rightHeaderContainer); 130 this.right.appendChild(this.layout.panes); 131 132 // Header 133 this.rightToolbar = moaiEditor.createHTML('<div id="moaied__phone_right_toolbar"></div>'); 134 this.rightButtons = moaiEditor.createHTML('<div id="moaied__phone_right_buttons" class="moaied-phone-row"><div></div><div></div></div>'); 135 this.rightHeaderDetachable.appendChild(this.rightButtons); 136 this.rightHeaderDetachable.appendChild(this.rightToolbar); 137 this.rightHeaderContainer.appendChild(this.rightHeaderDetachable); 138 139 // Editor buttons row (Preview, Save, Cancel, etc) 140 var left = this.rightButtons.firstChild; 141 var right = this.rightButtons.lastChild; 142 left.appendChild(this.btn_goLeft.handle); 143 right.appendChild(moaiEditor.buttons.preview.handle); 144 right.appendChild(moaiEditor.buttons.save.handle); 145 right.appendChild(moaiEditor.buttons.cancel.handle); 146 147 // Default Dokuwiki toolbar row 148 this.toolbar = elements.toolbar; 149 this.rightToolbar.prepend(this.toolbar); 150 151 // Handle the weird behavior of iOS browsers by having a detachable header which does not dissapear whenever the on-screen-keyboard is opened 152 this.height = { 153 rightToolbar: this.rightToolbar.getBoundingClientRect().height, 154 rightButtons: this.rightButtons.getBoundingClientRect().height, 155 }; 156 window.visualViewport.addEventListener('scroll', this.onVisualViewportChange.bind(this)); 157 window.visualViewport.addEventListener('resize', this.onVisualViewportChange.bind(this)); 158 159 // ─────────────── Cosmetic things ───────────────── 160 161 this.btn_goRight.handle.style.height = this.btn_goLeft.handle.getBoundingClientRect().height + 'px'; 162 163 // ─────────────────── Debug ─────────────────────── 164 165 // Debug line 166 /* 167 this.debugline = moaiEditor.createHTML('<div id="moaied__phone_debugline"></div>'); 168 this.rightToolbar.prepend(this.debugline); 169 var interval = setInterval(this.onDebugInterval.bind(this), 200); 170 */ 171 172 // ───────────── Swipes and scroll ───────────────── 173 174 /* 175 document.querySelector("#moaied__phone_right").addEventListener('touchmove', event => { 176 event.preventDefault(); 177 }); 178 */ 179 // Detect swipes 180 new MoaiEditor.Swipe(this.left, this.onSwipe.bind(this), 120); 181 new MoaiEditor.Swipe(this.rightButtons, this.onSwipe.bind(this), 120); 182 183 // Show a little bit of the preview (to let the user know there is a preview available) 184 this.layout.panes.scrollLeft = window.innerWidth/5; 185 } 186 187 onWindowResize() { 188 // Placeholder 189 } 190 191 onVisualViewportChange() { 192 193 // Handles 194 const container = this.rightHeaderContainer; 195 const detachable = this.rightHeaderDetachable; 196 197 // Detach the header and position it into view if the browser has iOS behavior 198 if (window.visualViewport.pageTop > 3 && window.visualViewport.scale < 1.3) { 199 detachable.classList.add('moaied-header-detached'); 200 detachable.style.top = window.visualViewport.pageTop+'px'; 201 } else 202 detachable.classList.remove('moaied-header-detached'); 203 204 // Hide the editor buttons if the vertical space is small (due to on-screen-keyboard opened on a small phone) 205 if (window.visualViewport.height < 380) 206 this.rightButtons.style.display = 'none'; 207 else 208 this.rightButtons.style.display = 'flex'; 209 } 210 211 onSwipe(direction) { 212 if (direction == 'left') 213 this.goRight(); 214 if (direction == 'right') 215 this.goLeft(); 216 } 217 goLeft() { 218 document.getElementById('moaied__wrapper').scrollLeft = 0; 219 } 220 goRight() { 221 document.getElementById('moaied__wrapper').scrollLeft = window.innerWidth; 222 } 223 224 onDebugInterval() { 225 const html = document.documentElement; 226 const body = document.body; 227 const htmlHeight = html.getBoundingClientRect().height; 228 const bodyHeight = body.getBoundingClientRect().height; 229 this.debugline.textContent = "innerHeight: "+window.innerHeight+" - Scroll: "+html.scrollTop+" - htmlH: "+htmlHeight+" - bodyH: "+bodyHeight; 230 } 231}; // End Class 232 233 234 235