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+'&nbsp;&nbsp;'+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