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