| | @@ -43,13 +43,10 @@ |
| 43 | 43 | //console.log(urlTop, aLoc); |
| 44 | 44 | for( const o of eSelect.options ){ |
| 45 | 45 | o.value = urlTop + (o.value || o.innerText); |
| 46 | 46 | } |
| 47 | 47 | |
| 48 | | - const eBtnPrev = E('#btn-prev'); |
| 49 | | - const eBtnNext = E('#btn-next'); |
| 50 | | - |
| 51 | 48 | const updateUrl = function(opt){ |
| 52 | 49 | if( opt ){ |
| 53 | 50 | let url = (opt.value || opt.innerText); |
| 54 | 51 | eCurrentUrl.innerText = url.replace(urlTop,''); |
| 55 | 52 | eCurrentUrl.setAttribute('href', url); |
| | @@ -64,18 +61,21 @@ |
| 64 | 61 | eIframe.setAttribute('src', so.value || so.innerText); |
| 65 | 62 | updateUrl(so); |
| 66 | 63 | } |
| 67 | 64 | }); |
| 68 | 65 | |
| 66 | + /** Select the entry at the given ndx and fire a change event. */ |
| 69 | 67 | const selectEntry = function(ndx){ |
| 70 | 68 | if( ndx>=0 ){ |
| 71 | 69 | eSelect.selectedIndex = ndx; |
| 72 | 70 | eSelect.dispatchEvent(new Event('change',{target:eSelect})); |
| 73 | 71 | } |
| 74 | 72 | }; |
| 75 | 73 | |
| 76 | | - const cycleLink = function(dir){ |
| 74 | + /* Cycle to the next link in the list, accounting for separators and |
| 75 | + wrapping around at either end. */ |
| 76 | + const cycleLink = function(dir/*<0 = prev, >0 = next*/){ |
| 77 | 77 | let n = eSelect.selectedIndex + dir; |
| 78 | 78 | if( n < 0 ) n = eSelect.options.length-1; |
| 79 | 79 | else if( n>=eSelect.options.length ){ |
| 80 | 80 | n = 0; |
| 81 | 81 | } |
| | @@ -87,12 +87,12 @@ |
| 87 | 87 | }else{ |
| 88 | 88 | selectEntry(n); |
| 89 | 89 | } |
| 90 | 90 | }; |
| 91 | 91 | |
| 92 | | - eBtnPrev.addEventListener('click', ()=>cycleLink(-1), false); |
| 93 | | - eBtnNext.addEventListener('click', ()=>cycleLink(1), false); |
| 92 | + E('#btn-prev').addEventListener('click', ()=>cycleLink(-1), false); |
| 93 | + E('#btn-next').addEventListener('click', ()=>cycleLink(1), false); |
| 94 | 94 | |
| 95 | 95 | /** |
| 96 | 96 | We have to adjust the iframe's size dynamically to account for |
| 97 | 97 | other widgets around it. iframes don't simply like to fill up all |
| 98 | 98 | available space without some help. If #controlWrapper only |
| | @@ -99,12 +99,12 @@ |
| 99 | 99 | contained the one SELECT element, CSS would be sufficient, but |
| 100 | 100 | once we add text around it, #controlWrapper's size becomes |
| 101 | 101 | unpredictable and we need JS to calculate it. We do this every |
| 102 | 102 | time the window size changes. |
| 103 | 103 | */ |
| 104 | | - // Copied from fossil.dom.js |
| 105 | 104 | const effectiveHeight = function f(e){ |
| 105 | + // Copied from fossil.dom.js |
| 106 | 106 | if(!e) return 0; |
| 107 | 107 | if(!f.measure){ |
| 108 | 108 | f.measure = function callee(e, depth){ |
| 109 | 109 | if(!e) return; |
| 110 | 110 | const m = e.getBoundingClientRect(); |
| | @@ -126,12 +126,14 @@ |
| 126 | 126 | f.extra = 0; |
| 127 | 127 | f.measure(e,0); |
| 128 | 128 | return f.extra; |
| 129 | 129 | }; |
| 130 | 130 | |
| 131 | | - // Copied from fossil.bootstrap.js |
| 131 | + /* Helper for the window-resized event handler below, to avoid |
| 132 | + handling the resize until after it's finished. */ |
| 132 | 133 | const debounce = function f(func, waitMs, immediate) { |
| 134 | + // Copied from fossil.bootstrap.js |
| 133 | 135 | var timeoutId; |
| 134 | 136 | if(!waitMs) waitMs = f.$defaultDelay; |
| 135 | 137 | return function() { |
| 136 | 138 | const context = this, args = Array.prototype.slice.call(arguments); |
| 137 | 139 | const later = function() { |
| | @@ -143,28 +145,26 @@ |
| 143 | 145 | timeoutId = setTimeout(later, waitMs); |
| 144 | 146 | if(callNow) func.apply(context, args); |
| 145 | 147 | }; |
| 146 | 148 | }; |
| 147 | 149 | |
| 150 | + /** |
| 151 | + Resize eConstrained (the ifame element) so that it fits within |
| 152 | + the page space not occupied by the list of elements eToAvoid. |
| 153 | + */ |
| 148 | 154 | const ForceResizeKludge = (function(eToAvoid, eConstrained){ |
| 149 | 155 | const resized = function f(){ |
| 150 | 156 | if( f.$disabled ) return; |
| 151 | 157 | const wh = window.innerHeight; |
| 152 | 158 | let ht; |
| 153 | 159 | let extra = 0; |
| 154 | 160 | eToAvoid.forEach((e)=>e ? extra += effectiveHeight(e) : false); |
| 155 | 161 | ht = wh - extra; |
| 156 | 162 | if( ht < 100 ) ht = 100; |
| 157 | | - eConstrained.style.top = 'calc('+extra+'px + 1.5em)'; |
| 163 | + eConstrained.style.top = 'calc('+extra+'px + 2em)'; |
| 158 | 164 | eConstrained.style.height = |
| 159 | | - eConstrained.style.maxHeight = [ |
| 160 | | - "calc(", ht, "px", |
| 161 | | - " - 0.65em"/*fudge value*/,")" |
| 162 | | - /* ^^^^ hypothetically not needed, but both Chrome/FF on |
| 163 | | - Linux will force scrollbars on the body if this value is |
| 164 | | - too small; current value is empirically selected. */ |
| 165 | | - ].join(''); |
| 165 | + eConstrained.style.maxHeight = "calc("+ ht+ "px - 2em)"; |
| 166 | 166 | }; |
| 167 | 167 | resized.$disabled = true/* gets deleted later */; |
| 168 | 168 | window.addEventListener('resize', debounce(resized, 250), false); |
| 169 | 169 | return resized; |
| 170 | 170 | })( |
| 171 | 171 | |