Diff
Not logged in

Differences From Artifact [de6495e6f5]:

To Artifact [5fdd6cd488]:


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