| | @@ -12,10 +12,12 @@ |
| 12 | 12 | |
| 13 | 13 | This is the main entry point for the WASM rendition of fossil's |
| 14 | 14 | /pikchrshow app. It sets up the various UI bits, loads a Worker for |
| 15 | 15 | the pikchr process, and manages the communication between the UI and |
| 16 | 16 | worker. |
| 17 | + |
| 18 | + API dependencies: fossil.dom, fossil.storage |
| 17 | 19 | */ |
| 18 | 20 | (function(F/*fossil object*/){ |
| 19 | 21 | 'use strict'; |
| 20 | 22 | |
| 21 | 23 | /* Recall that the 'self' symbol, except where locally |
| | @@ -159,19 +161,19 @@ |
| 159 | 161 | /** |
| 160 | 162 | The 'pikchrshow-ready' event is fired (with no payload) when the |
| 161 | 163 | wasm module has finished loading. */ |
| 162 | 164 | PS.addMsgHandler('pikchrshow-ready', function(){ |
| 163 | 165 | PS.clearMsgHandlers('pikchrshow-ready'); |
| 164 | | - F.onPikchrshowLoaded(); |
| 166 | + F.page.onPikchrshowLoaded(); |
| 165 | 167 | }); |
| 166 | 168 | |
| 167 | 169 | /** |
| 168 | 170 | Performs all app initialization which must wait until after the |
| 169 | 171 | worker module is loaded. This function removes itself when it's |
| 170 | 172 | called. |
| 171 | 173 | */ |
| 172 | | - F.onPikchrshowLoaded = function(){ |
| 174 | + F.page.onPikchrshowLoaded = function(){ |
| 173 | 175 | delete this.onPikchrshowLoaded; |
| 174 | 176 | // Unhide all elements which start out hidden |
| 175 | 177 | EAll('.initially-hidden').forEach((e)=>e.classList.remove('initially-hidden')); |
| 176 | 178 | const taInput = E('#input'); |
| 177 | 179 | const btnClearIn = E('#btn-clear'); |
| | @@ -195,10 +197,67 @@ |
| 195 | 197 | }; |
| 196 | 198 | btnRender.addEventListener('click',function(ev){ |
| 197 | 199 | ev.preventDefault(); |
| 198 | 200 | renderCurrentText(); |
| 199 | 201 | },false); |
| 202 | + |
| 203 | + 0 && (function(){ |
| 204 | + /* Set up split-view controls... This _almost_ works correctly, |
| 205 | + just needs some tweaking to account for switching between |
| 206 | + side-by-side and top-bottom views. */ |
| 207 | + // adapted from https://htmldom.dev/create-resizable-split-views/ |
| 208 | + const Split = { |
| 209 | + e:{ |
| 210 | + left: E('.zone-wrapper.input'), |
| 211 | + right: E('.zone-wrapper.output'), |
| 212 | + handle: E('.splitter-handle'), |
| 213 | + parent: E('#main-wrapper') |
| 214 | + }, |
| 215 | + x: 0, y: 0, |
| 216 | + widthLeft: 0, |
| 217 | + heightLeft: 0 |
| 218 | + }; |
| 219 | + Split.mouseDownHandler = function(e){ |
| 220 | + this.x = e.clientX; |
| 221 | + this.y = e.clientY; |
| 222 | + const r = this.e.left.getBoundingClientRect(); |
| 223 | + this.widthLeft = r.width; |
| 224 | + this.heightLeft = r.height; |
| 225 | + document.addEventListener('mousemove', this.mouseMoveHandler); |
| 226 | + document.addEventListener('mouseup', this.mouseUpHandler); |
| 227 | + }.bind(Split); |
| 228 | + Split.mouseMoveHandler = function(e){ |
| 229 | + const isHorizontal = this.e.parent.classList.contains('side-by-side'); |
| 230 | + const dx = e.clientX - this.x; |
| 231 | + const dy = e.clientY - this.y; |
| 232 | + if(isHorizontal){ |
| 233 | + const w = ((this.widthLeft + dx) * 100) |
| 234 | + / this.e.parent.getBoundingClientRect().width; |
| 235 | + this.e.left.style.width = w+'%'; |
| 236 | + }else{ |
| 237 | + const h = ((this.heightLeft + dy) * 100) |
| 238 | + / this.e.parent.getBoundingClientRect().height; |
| 239 | + this.e.left.style.height = h+'%'; |
| 240 | + } |
| 241 | + document.body.style.cursor = isHorizontal ? 'col-resize' : 'row-resize'; |
| 242 | + this.e.left.style.userSelect = 'none'; |
| 243 | + this.e.left.style.pointerEvents = 'none'; |
| 244 | + this.e.right.style.userSelect = 'none'; |
| 245 | + this.e.right.style.pointerEvents = 'none'; |
| 246 | + }.bind(Split); |
| 247 | + Split.mouseUpHandler = function(e){ |
| 248 | + this.e.handle.style.removeProperty('cursor'); |
| 249 | + document.body.style.removeProperty('cursor'); |
| 250 | + this.e.left.style.removeProperty('user-select'); |
| 251 | + this.e.left.style.removeProperty('pointer-events'); |
| 252 | + this.e.right.style.removeProperty('user-select'); |
| 253 | + this.e.right.style.removeProperty('pointer-events'); |
| 254 | + document.removeEventListener('mousemove', this.mouseMoveHandler); |
| 255 | + document.removeEventListener('mouseup', this.mouseUpHandler); |
| 256 | + }.bind(Split); |
| 257 | + Split.e.handle.addEventListener('mousedown', Split.mouseDownHandler); |
| 258 | + })(); |
| 200 | 259 | |
| 201 | 260 | /** To be called immediately before work is sent to the |
| 202 | 261 | worker. Updates some UI elements. The 'working'/'end' |
| 203 | 262 | event will apply the inverse, undoing the bits this |
| 204 | 263 | function does. This impl is not in the 'working'/'start' |
| | @@ -366,45 +425,10 @@ |
| 366 | 425 | legend.addEventListener('click', function(){ |
| 367 | 426 | fs.classList.toggle('collapsed'); |
| 368 | 427 | content.forEach((d)=>d.classList.toggle('hidden')); |
| 369 | 428 | }, false); |
| 370 | 429 | }); |
| 371 | | - |
| 372 | | - /** |
| 373 | | - Given a DOM element, this routine measures its "effective |
| 374 | | - height", which is the bounding top/bottom range of this element |
| 375 | | - and all of its children, recursively. For some DOM structure |
| 376 | | - cases, a parent may have a reported height of 0 even though |
| 377 | | - children have non-0 sizes. |
| 378 | | - |
| 379 | | - Returns 0 if !e or if the element really has no height. |
| 380 | | - */ |
| 381 | | - const effectiveHeight = function f(e){ |
| 382 | | - if(!e) return 0; |
| 383 | | - if(!f.measure){ |
| 384 | | - f.measure = function callee(e, depth){ |
| 385 | | - if(!e) return; |
| 386 | | - const m = e.getBoundingClientRect(); |
| 387 | | - if(0===depth){ |
| 388 | | - callee.top = m.top; |
| 389 | | - callee.bottom = m.bottom; |
| 390 | | - }else{ |
| 391 | | - callee.top = m.top ? Math.min(callee.top, m.top) : callee.top; |
| 392 | | - callee.bottom = Math.max(callee.bottom, m.bottom); |
| 393 | | - } |
| 394 | | - Array.prototype.forEach.call(e.children,(e)=>callee(e,depth+1)); |
| 395 | | - if(0===depth){ |
| 396 | | - //console.debug("measure() height:",e.className, callee.top, callee.bottom, (callee.bottom - callee.top)); |
| 397 | | - f.extra += callee.bottom - callee.top; |
| 398 | | - } |
| 399 | | - return f.extra; |
| 400 | | - }; |
| 401 | | - } |
| 402 | | - f.extra = 0; |
| 403 | | - f.measure(e,0); |
| 404 | | - return f.extra; |
| 405 | | - }; |
| 406 | 430 | |
| 407 | 431 | btnRender.click(); |
| 408 | 432 | |
| 409 | 433 | /** Debounce handler for auto-rendering while typing. */ |
| 410 | 434 | const debounceAutoRender = F.debounce(function f(){ |
| | @@ -462,11 +486,11 @@ |
| 462 | 486 | const resized = function f(){ |
| 463 | 487 | if(f.$disabled) return; |
| 464 | 488 | const wh = window.innerHeight; |
| 465 | 489 | var ht; |
| 466 | 490 | var extra = 0; |
| 467 | | - elemsToCount.forEach((e)=>e ? extra += effectiveHeight(e) : false); |
| 491 | + elemsToCount.forEach((e)=>e ? extra += F.dom.effectiveHeight(e) : false); |
| 468 | 492 | ht = wh - extra; |
| 469 | 493 | appViews.forEach(function(e){ |
| 470 | 494 | e.style.height = |
| 471 | 495 | e.style.maxHeight = [ |
| 472 | 496 | "calc(", (ht>=100 ? ht : 100), "px", |
| 473 | 497 | |