Fossil SCM

Hamburger menu enhancements: (1) Rename the JS to src/hbmenu.js (2) Make the JS independent of TH1 so that it can be loaded using builtin_request_js(). (3) Add a new TH1 command that invokes builtin_request_js(). (4) Revise the default and plain_gray skins to make use of the previous.

drh 2021-01-25 18:57 trunk merge
Commit 9cd74289c0a76606becb4bf66cac55f637c00dd9421acfd428992ec0fc2ca661
--- skins/default/footer.txt
+++ skins/default/footer.txt
@@ -1,8 +1,5 @@
11
<div class="footer">
22
This page was generated in about
33
<th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by
44
Fossil $release_version $manifest_version $manifest_date
55
</div>
6
-<script nonce="$nonce">
7
-<th1>styleScript</th1>
8
-</script>
96
--- skins/default/footer.txt
+++ skins/default/footer.txt
@@ -1,8 +1,5 @@
1 <div class="footer">
2 This page was generated in about
3 <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by
4 Fossil $release_version $manifest_version $manifest_date
5 </div>
6 <script nonce="$nonce">
7 <th1>styleScript</th1>
8 </script>
9
--- skins/default/footer.txt
+++ skins/default/footer.txt
@@ -1,8 +1,5 @@
1 <div class="footer">
2 This page was generated in about
3 <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by
4 Fossil $release_version $manifest_version $manifest_date
5 </div>
 
 
 
6
--- skins/default/header.txt
+++ skins/default/header.txt
@@ -18,10 +18,11 @@
1818
} else {
1919
html "<a href='$home$url' class='$cls'>$name</a>\n"
2020
}
2121
}
2222
html "<a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>&#9776;</a>"
23
+builtin_request_js hbmenu.js
2324
menulink $index_page Home {}
2425
if {[anycap jor]} {
2526
menulink /timeline Timeline {}
2627
}
2728
if {[hascap oh]} {
2829
2930
DELETED skins/default/js.txt
--- skins/default/header.txt
+++ skins/default/header.txt
@@ -18,10 +18,11 @@
18 } else {
19 html "<a href='$home$url' class='$cls'>$name</a>\n"
20 }
21 }
22 html "<a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>&#9776;</a>"
 
23 menulink $index_page Home {}
24 if {[anycap jor]} {
25 menulink /timeline Timeline {}
26 }
27 if {[hascap oh]} {
28
29 ELETED skins/default/js.txt
--- skins/default/header.txt
+++ skins/default/header.txt
@@ -18,10 +18,11 @@
18 } else {
19 html "<a href='$home$url' class='$cls'>$name</a>\n"
20 }
21 }
22 html "<a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>&#9776;</a>"
23 builtin_request_js hbmenu.js
24 menulink $index_page Home {}
25 if {[anycap jor]} {
26 menulink /timeline Timeline {}
27 }
28 if {[hascap oh]} {
29
30 ELETED skins/default/js.txt
D skins/default/js.txt
-228
--- a/skins/default/js.txt
+++ b/skins/default/js.txt
@@ -1,228 +0,0 @@
1
-/*
2
-** Copyright © 2018 Warren Young
3
-**
4
-** This program is free software; you can redistribute it and/or
5
-** modify it under the terms of the Simplified BSD License (also
6
-** known as the "2-Clause License" or "FreeBSD License".)
7
-**
8
-** This program is distributed in the hope that it will be useful,
9
-** but without any warranty; without even the implied warranty of
10
-** merchantability or fitness for a particular purpose.
11
-**
12
-** Contact: wyoung on the Fossil forum, https
13
-*******************************************************************************
14
-**
15
-** Thisspecific to the Fossil default skin.
16
-** Currently, the only thing this does is handle clicks on isition the hddrop container.
17
-*/
18
-(function() {
19
- var hbButton = document.getElementById("hbbtn");
20
- if (!hbButton) return; // no hamburger button
21
- if (!document.addEventListener) return; // Incompatible browser
22
- var panel = document.getElementById("hbdrop");
23
- if (!panel) return; // site admin might've nuked it
24
- if (!panel.style) return; // shouldn't happen, but be sure
25
- var panelBorder = panel.style.border;
26
- var panelInitialized = false; // reset if browser window is resized
27
- var panelResetBorderTimerID = 0; // used to cancel post-animation tasks
28
-
29
- // Disable animation if this browser doesn't support CSS transitions.
30
- //
31
- // We need this ugly calling form for old browsers that don't allow
32
- // panel.style.hasOwnProperty('transition'); catering to old browsers
33
- // is the whole point here.
34
- var animate = panel.style.transition !== null && (typeof(panel.style.transition) == "string");
35
-
36
- // The duration of the animation can be overridden from the default skin
37
- // header.txt by setting the "data-anim-ms" attribute of the panel.
38
- var animMS = panel.getAttribute("data-anim-ms");
39
- if (animMS) { // not null or empty string, parse it
40
- animMS = parseInt(animMS);
41
- if (isNaN(animMS) || animMS == 0)
42
- animate = false; // disable animation if non-numeric or zero
43
- else if (animMS < 0)
44
- animMS = 400; // set default animation duration if negative
45
- }
46
- else // attribute is null or empty string, use default
47
- animMS = 400;
48
-
49
- // Calculate panel height despite its being hidden at call time.
50
- // Based on https://stackoverflow.com/a/29047447/142454
51
- var panelHeight; // computed on first panel display
52
- function calculatePanelHeight() {
53
-
54
- // Clear the max-height CSS property in case the panel size is recalculated
55
- // after the browser window was resized.
56
- panel.style.maxHeight = '';
57
-
58
- // Get initial panel styles so we can restore them below.
59
- var es = window.getComputedStyle(panel),
60
- edis = es.display,
61
- epos = es.position,
62
- evis = es.visibility;
63
-
64
- // Restyle the panel so we can measure its height while invisible.
65
- panel.style.visibility = 'hidden';
66
- panel.style.position = 'absolute';
67
- panel.style.display = 'block';
68
- panelHeight = panel.offsetHeight + 'px';
69
-
70
- // Revert styles now that job is done.
71
- panel.style.display = edis;
72
- panel.style.position = epos;
73
- panel.style.visibility = evis;
74
- }
75
-
76
- // Show the panel by changing the panel height, which kicks off the
77
- // slide-open/closed transition set up in the XHR onload handler.
78
- //
79
- // Schedule the change for a near-future time in case this is the
80
- // first call, where the div was initially invisible. If we were
81
- // to change the panel's visibility and height at the same time
82
- // instead, that would prevent the browser from seeing the height
83
- // change as a state transition, so it'd skip the CSS transition:
84
- //
85
- // https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions#JavaScript_examples
86
- function showPanel() {
87
- // Cancel the timer to remove the panel border after the closing animation,
88
- // otherwise double-clicking the hamburger button with the panel opened will
89
- // remove the borders from the (closed and immediately reopened) panel.
90
- if (panelResetBorderTimerID) {
91
- clearTimeout(panelResetBorderTimerID);
92
- panelResetBorderTimerID = 0;
93
- }
94
- if (animate) {
95
- if (!panelInitialized) {
96
- panelInitialized = true;
97
- // Set up a CSS transition to animate the panel open and
98
- // closed. Only needs to be done once per page load.
99
- // Based on https://stackoverflow.com/a/29047447/142454
100
- calculatePanelHeight();
101
- panel.style.transition = 'max-height ' + animMS +
102
- 'ms ease-in-out';
103
- panel.style.overflowY = 'hidden';
104
- panel.style.maxHeight = '0';
105
- }
106
- setTimeout(function() {
107
- panel.style.maxHeight = panelHeight;
108
- panel.style.border = panelBorder;
109
- }, 40); // 25ms is insufficient with Firefox 62
110
- }
111
- panel.style.display = 'block';
112
- document.addEventListener('keydown',panelKeydown,/* useCapture == */true);
113
- document.addEventListener('click',panelClick,false);
114
- }
115
-
116
- var panelKeydown = function(event) {
117
- var key = event.which || event.keyCode;
118
- if (key == 27) {
119
- event.stopPropagation(); // ignore other keydown handlers
120
- panelToggle(true);
121
- }
122
- };
123
-
124
- var panelClick = function(event) {
125
- if (!panel.contains(event.target)) {
126
- // Call event.preventDefault() to have clicks outside the opened panel
127
- // just close the panel, and swallow clicks on links or form elements.
128
- //event.preventDefault();
129
- panelToggle(true);
130
- }
131
- };
132
-
133
- // Return true if the panel is showing.
134
- function panelShowing() {
135
- if (animate) {
136
- return panel.style.maxHeight == panelHeight;
137
- }
138
- else {
139
- return panel.style.display == 'block';
140
- }
141
- }
142
-
143
- // Check if the specified HTML element has any child elements. Note that plain
144
- // text nodes, comments, and any spaces (presentational or not) are ignored.
145
- function hasChildren(element) {
146
- var childElement = element.firstChild;
147
- while (childElement) {
148
- if (childElement.nodeType == 1) // Node.ELEMENT_NODE == 1
149
- return true;
150
- childElement = childElement.nextSibling;
151
- }
152
- return false;
153
- }
154
-
155
- // Reset the state of the panel to uninitialized if the browser window is
156
- // resized, so the dimensions are recalculated the next time it's opened.
157
- window.addEventListener('resize',function(event) {
158
- panelInitialized = false;
159
- },false);
160
-
161
- // Click handler for the hamburger button.
162
- hbButton.addEventListener('click',function(event) {
163
- // Break the event handler chain, or the handler for document → click
164
- // (about to be installed) may already be triggered by the current event.
165
- event.stopPropagation();
166
- event.preventDefault(); // prevent browser from acting on <a> click
167
- panelToggle(false);
168
- },false);
169
-
170
- function panelToggle(suppressAnimation) {
171
- if (panelShowing()) {
172
- document.removeEventListener('keydown',panelKeydown,/* useCapture == */true);
173
- document.removeEventListener('click',panelClick,false);
174
- // Transition back to hidden state.
175
- if (animate) {
176
- if (suppressAnimation) {
177
- var transition = panel.style.transition;
178
- panel.style.transition = '';
179
- panel.style.maxHeight = '0';
180
- panel.style.border = 'none';
181
- setTimeout(function() {
182
- // Make sure CSS transition won't take effect now, so restore it
183
- // asynchronously. Outer variable 'transition' still valid here.
184
- panel.style.transition = transition;
185
- }, 40); // 25ms is insufficient with Firefox 62
186
- }
187
- else {
188
- panel.style.maxHeight = '0';
189
- panelResetBorderTimerID = setTimeout(function() {
190
- // Browsers show a 1px high border line when maxHeight == 0,
191
- // our "hidden" state, so hide the borders in that state, too.
192
- panel.style.border = 'none';
193
- panelResetBorderTimerID = 0; // clear ID of completed timer
194
- }, animMS);
195
- }
196
- }
197
- else {
198
- panel.style.display = 'none';
199
- }
200
- }
201
- else {
202
- if (!hasChildren(panel)) {
203
- // Only get the sitemap once per page load: it isn't likely to
204
- // change on us.
205
- var xhr = new XMLHttpRequest();
206
- xhr.onload = function() {
207
- var doc = xhr.responseXML;
208
- if (doc) {
209
- var sm = doc.querySelector("ul#sitemap");
210
- if (sm && xhr.status == 200) {
211
- // Got sitemap. Insert it into the drop-down panel.
212
- panel.innerHTML = sm.outerHTML;
213
- // Display the panel
214
- showPanel();
215
- }
216
- }
217
- // else, can't parse response as HTML or XML
218
- }
219
- xhr.open("GET xhr.open("GET", url);
220
- xhr.responseType = "document";
221
- xhr.send();
222
- }
223
- else {
224
- showPanel(); // just show what we built abo{
225
- // Turn the button into ncompatible browser
226
- var panel = document.getElementById("hbdrop");
227
- if (!panel) return; // site admin might've nuked it
228
- if (!panel.style) return; // shouldn't happen, but b
--- a/skins/default/js.txt
+++ b/skins/default/js.txt
@@ -1,228 +0,0 @@
1 /*
2 ** Copyright © 2018 Warren Young
3 **
4 ** This program is free software; you can redistribute it and/or
5 ** modify it under the terms of the Simplified BSD License (also
6 ** known as the "2-Clause License" or "FreeBSD License".)
7 **
8 ** This program is distributed in the hope that it will be useful,
9 ** but without any warranty; without even the implied warranty of
10 ** merchantability or fitness for a particular purpose.
11 **
12 ** Contact: wyoung on the Fossil forum, https
13 *******************************************************************************
14 **
15 ** Thisspecific to the Fossil default skin.
16 ** Currently, the only thing this does is handle clicks on isition the hddrop container.
17 */
18 (function() {
19 var hbButton = document.getElementById("hbbtn");
20 if (!hbButton) return; // no hamburger button
21 if (!document.addEventListener) return; // Incompatible browser
22 var panel = document.getElementById("hbdrop");
23 if (!panel) return; // site admin might've nuked it
24 if (!panel.style) return; // shouldn't happen, but be sure
25 var panelBorder = panel.style.border;
26 var panelInitialized = false; // reset if browser window is resized
27 var panelResetBorderTimerID = 0; // used to cancel post-animation tasks
28
29 // Disable animation if this browser doesn't support CSS transitions.
30 //
31 // We need this ugly calling form for old browsers that don't allow
32 // panel.style.hasOwnProperty('transition'); catering to old browsers
33 // is the whole point here.
34 var animate = panel.style.transition !== null && (typeof(panel.style.transition) == "string");
35
36 // The duration of the animation can be overridden from the default skin
37 // header.txt by setting the "data-anim-ms" attribute of the panel.
38 var animMS = panel.getAttribute("data-anim-ms");
39 if (animMS) { // not null or empty string, parse it
40 animMS = parseInt(animMS);
41 if (isNaN(animMS) || animMS == 0)
42 animate = false; // disable animation if non-numeric or zero
43 else if (animMS < 0)
44 animMS = 400; // set default animation duration if negative
45 }
46 else // attribute is null or empty string, use default
47 animMS = 400;
48
49 // Calculate panel height despite its being hidden at call time.
50 // Based on https://stackoverflow.com/a/29047447/142454
51 var panelHeight; // computed on first panel display
52 function calculatePanelHeight() {
53
54 // Clear the max-height CSS property in case the panel size is recalculated
55 // after the browser window was resized.
56 panel.style.maxHeight = '';
57
58 // Get initial panel styles so we can restore them below.
59 var es = window.getComputedStyle(panel),
60 edis = es.display,
61 epos = es.position,
62 evis = es.visibility;
63
64 // Restyle the panel so we can measure its height while invisible.
65 panel.style.visibility = 'hidden';
66 panel.style.position = 'absolute';
67 panel.style.display = 'block';
68 panelHeight = panel.offsetHeight + 'px';
69
70 // Revert styles now that job is done.
71 panel.style.display = edis;
72 panel.style.position = epos;
73 panel.style.visibility = evis;
74 }
75
76 // Show the panel by changing the panel height, which kicks off the
77 // slide-open/closed transition set up in the XHR onload handler.
78 //
79 // Schedule the change for a near-future time in case this is the
80 // first call, where the div was initially invisible. If we were
81 // to change the panel's visibility and height at the same time
82 // instead, that would prevent the browser from seeing the height
83 // change as a state transition, so it'd skip the CSS transition:
84 //
85 // https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions#JavaScript_examples
86 function showPanel() {
87 // Cancel the timer to remove the panel border after the closing animation,
88 // otherwise double-clicking the hamburger button with the panel opened will
89 // remove the borders from the (closed and immediately reopened) panel.
90 if (panelResetBorderTimerID) {
91 clearTimeout(panelResetBorderTimerID);
92 panelResetBorderTimerID = 0;
93 }
94 if (animate) {
95 if (!panelInitialized) {
96 panelInitialized = true;
97 // Set up a CSS transition to animate the panel open and
98 // closed. Only needs to be done once per page load.
99 // Based on https://stackoverflow.com/a/29047447/142454
100 calculatePanelHeight();
101 panel.style.transition = 'max-height ' + animMS +
102 'ms ease-in-out';
103 panel.style.overflowY = 'hidden';
104 panel.style.maxHeight = '0';
105 }
106 setTimeout(function() {
107 panel.style.maxHeight = panelHeight;
108 panel.style.border = panelBorder;
109 }, 40); // 25ms is insufficient with Firefox 62
110 }
111 panel.style.display = 'block';
112 document.addEventListener('keydown',panelKeydown,/* useCapture == */true);
113 document.addEventListener('click',panelClick,false);
114 }
115
116 var panelKeydown = function(event) {
117 var key = event.which || event.keyCode;
118 if (key == 27) {
119 event.stopPropagation(); // ignore other keydown handlers
120 panelToggle(true);
121 }
122 };
123
124 var panelClick = function(event) {
125 if (!panel.contains(event.target)) {
126 // Call event.preventDefault() to have clicks outside the opened panel
127 // just close the panel, and swallow clicks on links or form elements.
128 //event.preventDefault();
129 panelToggle(true);
130 }
131 };
132
133 // Return true if the panel is showing.
134 function panelShowing() {
135 if (animate) {
136 return panel.style.maxHeight == panelHeight;
137 }
138 else {
139 return panel.style.display == 'block';
140 }
141 }
142
143 // Check if the specified HTML element has any child elements. Note that plain
144 // text nodes, comments, and any spaces (presentational or not) are ignored.
145 function hasChildren(element) {
146 var childElement = element.firstChild;
147 while (childElement) {
148 if (childElement.nodeType == 1) // Node.ELEMENT_NODE == 1
149 return true;
150 childElement = childElement.nextSibling;
151 }
152 return false;
153 }
154
155 // Reset the state of the panel to uninitialized if the browser window is
156 // resized, so the dimensions are recalculated the next time it's opened.
157 window.addEventListener('resize',function(event) {
158 panelInitialized = false;
159 },false);
160
161 // Click handler for the hamburger button.
162 hbButton.addEventListener('click',function(event) {
163 // Break the event handler chain, or the handler for document → click
164 // (about to be installed) may already be triggered by the current event.
165 event.stopPropagation();
166 event.preventDefault(); // prevent browser from acting on <a> click
167 panelToggle(false);
168 },false);
169
170 function panelToggle(suppressAnimation) {
171 if (panelShowing()) {
172 document.removeEventListener('keydown',panelKeydown,/* useCapture == */true);
173 document.removeEventListener('click',panelClick,false);
174 // Transition back to hidden state.
175 if (animate) {
176 if (suppressAnimation) {
177 var transition = panel.style.transition;
178 panel.style.transition = '';
179 panel.style.maxHeight = '0';
180 panel.style.border = 'none';
181 setTimeout(function() {
182 // Make sure CSS transition won't take effect now, so restore it
183 // asynchronously. Outer variable 'transition' still valid here.
184 panel.style.transition = transition;
185 }, 40); // 25ms is insufficient with Firefox 62
186 }
187 else {
188 panel.style.maxHeight = '0';
189 panelResetBorderTimerID = setTimeout(function() {
190 // Browsers show a 1px high border line when maxHeight == 0,
191 // our "hidden" state, so hide the borders in that state, too.
192 panel.style.border = 'none';
193 panelResetBorderTimerID = 0; // clear ID of completed timer
194 }, animMS);
195 }
196 }
197 else {
198 panel.style.display = 'none';
199 }
200 }
201 else {
202 if (!hasChildren(panel)) {
203 // Only get the sitemap once per page load: it isn't likely to
204 // change on us.
205 var xhr = new XMLHttpRequest();
206 xhr.onload = function() {
207 var doc = xhr.responseXML;
208 if (doc) {
209 var sm = doc.querySelector("ul#sitemap");
210 if (sm && xhr.status == 200) {
211 // Got sitemap. Insert it into the drop-down panel.
212 panel.innerHTML = sm.outerHTML;
213 // Display the panel
214 showPanel();
215 }
216 }
217 // else, can't parse response as HTML or XML
218 }
219 xhr.open("GET xhr.open("GET", url);
220 xhr.responseType = "document";
221 xhr.send();
222 }
223 else {
224 showPanel(); // just show what we built abo{
225 // Turn the button into ncompatible browser
226 var panel = document.getElementById("hbdrop");
227 if (!panel) return; // site admin might've nuked it
228 if (!panel.style) return; // shouldn't happen, but b
--- a/skins/default/js.txt
+++ b/skins/default/js.txt
@@ -1,228 +0,0 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- skins/plain_gray/header.txt
+++ skins/plain_gray/header.txt
@@ -2,10 +2,11 @@
22
<div class="title">$<project_name>: $<title></div>
33
</div>
44
<div class="mainmenu">
55
<th1>
66
html "<a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>&#9776;</a>"
7
+builtin_request_js hbmenu.js
78
html "<a href='$home$index_page'>Home</a>\n"
89
if {[anycap jor]} {
910
html "<a href='$home/timeline'>Timeline</a>\n"
1011
}
1112
if {[anoncap oh]} {
@@ -32,8 +33,5 @@
3233
} else {
3334
html "<a href='$home/login'>Login</a>\n"
3435
}
3536
</th1></div>
3637
<div id='hbdrop' class='hbdrop'></div>
37
-<script nonce="$nonce">
38
-<th1>styleScript skins/default/js.txt</th1>
39
-</script>
4038
4139
ADDED src/hbmenu.js
--- skins/plain_gray/header.txt
+++ skins/plain_gray/header.txt
@@ -2,10 +2,11 @@
2 <div class="title">$<project_name>: $<title></div>
3 </div>
4 <div class="mainmenu">
5 <th1>
6 html "<a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>&#9776;</a>"
 
7 html "<a href='$home$index_page'>Home</a>\n"
8 if {[anycap jor]} {
9 html "<a href='$home/timeline'>Timeline</a>\n"
10 }
11 if {[anoncap oh]} {
@@ -32,8 +33,5 @@
32 } else {
33 html "<a href='$home/login'>Login</a>\n"
34 }
35 </th1></div>
36 <div id='hbdrop' class='hbdrop'></div>
37 <script nonce="$nonce">
38 <th1>styleScript skins/default/js.txt</th1>
39 </script>
40
41 DDED src/hbmenu.js
--- skins/plain_gray/header.txt
+++ skins/plain_gray/header.txt
@@ -2,10 +2,11 @@
2 <div class="title">$<project_name>: $<title></div>
3 </div>
4 <div class="mainmenu">
5 <th1>
6 html "<a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>&#9776;</a>"
7 builtin_request_js hbmenu.js
8 html "<a href='$home$index_page'>Home</a>\n"
9 if {[anycap jor]} {
10 html "<a href='$home/timeline'>Timeline</a>\n"
11 }
12 if {[anoncap oh]} {
@@ -32,8 +33,5 @@
33 } else {
34 html "<a href='$home/login'>Login</a>\n"
35 }
36 </th1></div>
37 <div id='hbdrop' class='hbdrop'></div>
 
 
 
38
39 DDED src/hbmenu.js
+31 -9
--- a/src/hbmenu.js
+++ b/src/hbmenu.js
@@ -1,5 +1,5 @@
11
/*
2
-** Copyright © 2018 Warren Young
2
+** Originally: Copyright © 2018 Warren Young
33
**
44
** This program is free software; you can redistribute it and/or
55
** modify it under the terms of the Simplified BSD License (also
@@ -9,11 +9,37 @@
99
** but without any warranty; without even the implied warranty of
1010
** merchantability or fitness for a particular purpose.
1111
**
12
-** Contact: wyoung on the Fossil forum, https
12
+** Contact: wyoung on the Fossil forum, https://fossil-scm.org/forum/
13
+** Modified by others.
14
+**
1315
*******************************************************************************
1416
**
15
-** Thisspecific to the Fossil default skin.
16
-** Currently, the only thing this does is handle clicks on isition the hddrop container.
17
+** This file contains the JS code used to implement the expanding hamburger
18
+** menu on various skins.
19
+**
20
+** This was original the "js.txt" file for the default skin. It was subsequently
21
+** moved into src/hbmenu.js so that it could be more easily reused by other skins
22
+** using the "builtin_request_js" TH1 command.
23
+**
24
+** Operation:
25
+**
26
+** This script request that the HTML contain two elements:
27
+**
28
+** <a id="hbbtn"> <--- The hambdiger menu button
29
+** <nav id="hbdrop"> <--- Container for the hamburger menu
30
+**
31
+** Bindings are made on hbbtn so that when it is clicked, the following
32
+** happens:
33
+**
34
+** 1. An XHR is made to /sitemap?popup to fetch the HTML for the
35
+** popup menu.
36
+**
37
+** 2. The HTML for the popup is inserted into hddrop.
38
+**
39
+** 3. The hddrop container is made visible.
40
+**
41
+** CSS rules are also needed to cause the hddrop to be initially invisible,
42
+** and to correctly style and position the hddrop container.
1743
*/
1844
(function() {
1945
var hbButton = document.getElementById("hbbtn");
@@ -221,8 +247,4 @@
221247
xhr.send();
222248
}
223249
else {
224
- showPanel(); // just show what we built abo{
225
- // Turn the button into ncompatible browser
226
- var panel = document.getElementById("hbdrop");
227
- if (!panel) return; // site admin might've nuked it
228
- if (!panel.style) return; // shouldn't happen, but b
250
+ showPanel(); // just show what we built abo
--- a/src/hbmenu.js
+++ b/src/hbmenu.js
@@ -1,5 +1,5 @@
1 /*
2 ** Copyright © 2018 Warren Young
3 **
4 ** This program is free software; you can redistribute it and/or
5 ** modify it under the terms of the Simplified BSD License (also
@@ -9,11 +9,37 @@
9 ** but without any warranty; without even the implied warranty of
10 ** merchantability or fitness for a particular purpose.
11 **
12 ** Contact: wyoung on the Fossil forum, https
 
 
13 *******************************************************************************
14 **
15 ** Thisspecific to the Fossil default skin.
16 ** Currently, the only thing this does is handle clicks on isition the hddrop container.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17 */
18 (function() {
19 var hbButton = document.getElementById("hbbtn");
@@ -221,8 +247,4 @@
221 xhr.send();
222 }
223 else {
224 showPanel(); // just show what we built abo{
225 // Turn the button into ncompatible browser
226 var panel = document.getElementById("hbdrop");
227 if (!panel) return; // site admin might've nuked it
228 if (!panel.style) return; // shouldn't happen, but b
--- a/src/hbmenu.js
+++ b/src/hbmenu.js
@@ -1,5 +1,5 @@
1 /*
2 ** Originally: Copyright © 2018 Warren Young
3 **
4 ** This program is free software; you can redistribute it and/or
5 ** modify it under the terms of the Simplified BSD License (also
@@ -9,11 +9,37 @@
9 ** but without any warranty; without even the implied warranty of
10 ** merchantability or fitness for a particular purpose.
11 **
12 ** Contact: wyoung on the Fossil forum, https://fossil-scm.org/forum/
13 ** Modified by others.
14 **
15 *******************************************************************************
16 **
17 ** This file contains the JS code used to implement the expanding hamburger
18 ** menu on various skins.
19 **
20 ** This was original the "js.txt" file for the default skin. It was subsequently
21 ** moved into src/hbmenu.js so that it could be more easily reused by other skins
22 ** using the "builtin_request_js" TH1 command.
23 **
24 ** Operation:
25 **
26 ** This script request that the HTML contain two elements:
27 **
28 ** <a id="hbbtn"> <--- The hambdiger menu button
29 ** <nav id="hbdrop"> <--- Container for the hamburger menu
30 **
31 ** Bindings are made on hbbtn so that when it is clicked, the following
32 ** happens:
33 **
34 ** 1. An XHR is made to /sitemap?popup to fetch the HTML for the
35 ** popup menu.
36 **
37 ** 2. The HTML for the popup is inserted into hddrop.
38 **
39 ** 3. The hddrop container is made visible.
40 **
41 ** CSS rules are also needed to cause the hddrop to be initially invisible,
42 ** and to correctly style and position the hddrop container.
43 */
44 (function() {
45 var hbButton = document.getElementById("hbbtn");
@@ -221,8 +247,4 @@
247 xhr.send();
248 }
249 else {
250 showPanel(); // just show what we built abo
 
 
 
 
+250
--- a/src/hbmenu.js
+++ b/src/hbmenu.js
@@ -0,0 +1,250 @@
1
+/*
2
+** Originally: Copyright © 2018 Warren Young
3
+**
4
+** This program is free software; you can redistribute it and/or
5
+** modify it under the terms of the Simplified BSD License (also
6
+** known as the "2-Clause License" or "FreeBSD License".)
7
+**
8
+** This program is distributed in the hope that it will be useful,
9
+** but without any warranty; without even the implied warranty of
10
+** merchantability or fitness for a particular purpose.
11
+**
12
+** Contact: wyoung on the Fossil forum, https://fossil-scm.org/forum/
13
+** Modified by others.
14
+**
15
+*******************************************************************************
16
+**
17
+** This file contains the JS code used to implement the expanding hamburger
18
+** menu on various skins.
19
+**
20
+** This was original the "js.txt" file for the default skin. It was subsequently
21
+** moved into src/hbmenu.js so that it could be more easily reused by other skins
22
+** using the "builtin_request_js" TH1 command.
23
+**
24
+** Operation:
25
+**
26
+** This script request that the HTML contain two elements:
27
+**
28
+** <a id="hbbtn"> <--- The hambdiger menu button
29
+** <nav id="hbdrop"> <--- Container for the hamburger menu
30
+**
31
+** Bindings are made on hbbtn so that when it is clicked, the following
32
+** happens:
33
+**
34
+** 1. An XHR is made to /sitemap?popup to fetch the HTML for the
35
+** popup menu.
36
+**
37
+** 2. The HTML for the popup is inserted into hddrop.
38
+**
39
+** 3. The hddrop container is made visible.
40
+**
41
+** CSS rules are also needed to cause the hddrop to be initially invisible,
42
+** and to correctly style and position the hddrop container.
43
+*/
44
+(function() {
45
+ var hbButton = document.getElementById("hbbtn");
46
+ if (!hbButton) return; // no hamburger button
47
+ if (!document.addEventListener) return; // Incompatible browser
48
+ var panel = document.getElementById("hbdrop");
49
+ if (!panel) return; // site admin might've nuked it
50
+ if (!panel.style) return; // shouldn't happen, but be sure
51
+ var panelBorder = panel.style.border;
52
+ var panelInitialized = false; // reset if browser window is resized
53
+ var panelResetBorderTimerID = 0; // used to cancel post-animation tasks
54
+
55
+ // Disable animation if this browser doesn't support CSS transitions.
56
+ //
57
+ // We need this ugly calling form for old browsers that don't allow
58
+ // panel.style.hasOwnProperty('transition'); catering to old browsers
59
+ // is the whole point here.
60
+ var animate = panel.style.transition !== null && (typeof(panel.style.transition) == "string");
61
+
62
+ // The duration of the animation can be overridden from the default skin
63
+ // header.txt by setting the "data-anim-ms" attribute of the panel.
64
+ var animMS = panel.getAttribute("data-anim-ms");
65
+ if (animMS) { // not null or empty string, parse it
66
+ animMS = parseInt(animMS);
67
+ if (isNaN(animMS) || animMS == 0)
68
+ animate = false; // disable animation if non-numeric or zero
69
+ else if (animMS < 0)
70
+ animMS = 400; // set default animation duration if negative
71
+ }
72
+ else // attribute is null or empty string, use default
73
+ animMS = 400;
74
+
75
+ // Calculate panel height despite its being hidden at call time.
76
+ // Based on https://stackoverflow.com/a/29047447/142454
77
+ var panelHeight; // computed on first panel display
78
+ function calculatePanelHeight() {
79
+
80
+ // Clear the max-height CSS property in case the panel size is recalculated
81
+ // after the browser window was resized.
82
+ panel.style.maxHeight = '';
83
+
84
+ // Get initial panel styles so we can restore them below.
85
+ var es = window.getComputedStyle(panel),
86
+ edis = es.display,
87
+ epos = es.position,
88
+ evis = es.visibility;
89
+
90
+ // Restyle the panel so we can measure its height while invisible.
91
+ panel.style.visibility = 'hidden';
92
+ panel.style.position = 'absolute';
93
+ panel.style.display = 'block';
94
+ panelHeight = panel.offsetHeight + 'px';
95
+
96
+ // Revert styles now that job is done.
97
+ panel.style.display = edis;
98
+ panel.style.position = epos;
99
+ panel.style.visibility = evis;
100
+ }
101
+
102
+ // Show the panel by changing the panel height, which kicks off the
103
+ // slide-open/closed transition set up in the XHR onload handler.
104
+ //
105
+ // Schedule the change for a near-future time in case this is the
106
+ // first call, where the div was initially invisible. If we were
107
+ // to change the panel's visibility and height at the same time
108
+ // instead, that would prevent the browser from seeing the height
109
+ // change as a state transition, so it'd skip the CSS transition:
110
+ //
111
+ // https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions#JavaScript_examples
112
+ function showPanel() {
113
+ // Cancel the timer to remove the panel border after the closing animation,
114
+ // otherwise double-clicking the hamburger button with the panel opened will
115
+ // remove the borders from the (closed and immediately reopened) panel.
116
+ if (panelResetBorderTimerID) {
117
+ clearTimeout(panelResetBorderTimerID);
118
+ panelResetBorderTimerID = 0;
119
+ }
120
+ if (animate) {
121
+ if (!panelInitialized) {
122
+ panelInitialized = true;
123
+ // Set up a CSS transition to animate the panel open and
124
+ // closed. Only needs to be done once per page load.
125
+ // Based on https://stackoverflow.com/a/29047447/142454
126
+ calculatePanelHeight();
127
+ panel.style.transition = 'max-height ' + animMS +
128
+ 'ms ease-in-out';
129
+ panel.style.overflowY = 'hidden';
130
+ panel.style.maxHeight = '0';
131
+ }
132
+ setTimeout(function() {
133
+ panel.style.maxHeight = panelHeight;
134
+ panel.style.border = panelBorder;
135
+ }, 40); // 25ms is insufficient with Firefox 62
136
+ }
137
+ panel.style.display = 'block';
138
+ document.addEventListener('keydown',panelKeydown,/* useCapture == */true);
139
+ document.addEventListener('click',panelClick,false);
140
+ }
141
+
142
+ var panelKeydown = function(event) {
143
+ var key = event.which || event.keyCode;
144
+ if (key == 27) {
145
+ event.stopPropagation(); // ignore other keydown handlers
146
+ panelToggle(true);
147
+ }
148
+ };
149
+
150
+ var panelClick = function(event) {
151
+ if (!panel.contains(event.target)) {
152
+ // Call event.preventDefault() to have clicks outside the opened panel
153
+ // just close the panel, and swallow clicks on links or form elements.
154
+ //event.preventDefault();
155
+ panelToggle(true);
156
+ }
157
+ };
158
+
159
+ // Return true if the panel is showing.
160
+ function panelShowing() {
161
+ if (animate) {
162
+ return panel.style.maxHeight == panelHeight;
163
+ }
164
+ else {
165
+ return panel.style.display == 'block';
166
+ }
167
+ }
168
+
169
+ // Check if the specified HTML element has any child elements. Note that plain
170
+ // text nodes, comments, and any spaces (presentational or not) are ignored.
171
+ function hasChildren(element) {
172
+ var childElement = element.firstChild;
173
+ while (childElement) {
174
+ if (childElement.nodeType == 1) // Node.ELEMENT_NODE == 1
175
+ return true;
176
+ childElement = childElement.nextSibling;
177
+ }
178
+ return false;
179
+ }
180
+
181
+ // Reset the state of the panel to uninitialized if the browser window is
182
+ // resized, so the dimensions are recalculated the next time it's opened.
183
+ window.addEventListener('resize',function(event) {
184
+ panelInitialized = false;
185
+ },false);
186
+
187
+ // Click handler for the hamburger button.
188
+ hbButton.addEventListener('click',function(event) {
189
+ // Break the event handler chain, or the handler for document → click
190
+ // (about to be installed) may already be triggered by the current event.
191
+ event.stopPropagation();
192
+ event.preventDefault(); // prevent browser from acting on <a> click
193
+ panelToggle(false);
194
+ },false);
195
+
196
+ function panelToggle(suppressAnimation) {
197
+ if (panelShowing()) {
198
+ document.removeEventListener('keydown',panelKeydown,/* useCapture == */true);
199
+ document.removeEventListener('click',panelClick,false);
200
+ // Transition back to hidden state.
201
+ if (animate) {
202
+ if (suppressAnimation) {
203
+ var transition = panel.style.transition;
204
+ panel.style.transition = '';
205
+ panel.style.maxHeight = '0';
206
+ panel.style.border = 'none';
207
+ setTimeout(function() {
208
+ // Make sure CSS transition won't take effect now, so restore it
209
+ // asynchronously. Outer variable 'transition' still valid here.
210
+ panel.style.transition = transition;
211
+ }, 40); // 25ms is insufficient with Firefox 62
212
+ }
213
+ else {
214
+ panel.style.maxHeight = '0';
215
+ panelResetBorderTimerID = setTimeout(function() {
216
+ // Browsers show a 1px high border line when maxHeight == 0,
217
+ // our "hidden" state, so hide the borders in that state, too.
218
+ panel.style.border = 'none';
219
+ panelResetBorderTimerID = 0; // clear ID of completed timer
220
+ }, animMS);
221
+ }
222
+ }
223
+ else {
224
+ panel.style.display = 'none';
225
+ }
226
+ }
227
+ else {
228
+ if (!hasChildren(panel)) {
229
+ // Only get the sitemap once per page load: it isn't likely to
230
+ // change on us.
231
+ var xhr = new XMLHttpRequest();
232
+ xhr.onload = function() {
233
+ var doc = xhr.responseXML;
234
+ if (doc) {
235
+ var sm = doc.querySelector("ul#sitemap");
236
+ if (sm && xhr.status == 200) {
237
+ // Got sitemap. Insert it into the drop-down panel.
238
+ panel.innerHTML = sm.outerHTML;
239
+ // Display the panel
240
+ showPanel();
241
+ }
242
+ }
243
+ // else, can't parse response as HTML or XML
244
+ }
245
+ xhr.open("GET xhr.open("GET", url);
246
+ xhr.responseType = "document";
247
+ xhr.send();
248
+ }
249
+ else {
250
+ showPanel(); // just show what we built abo
--- a/src/hbmenu.js
+++ b/src/hbmenu.js
@@ -0,0 +1,250 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/src/hbmenu.js
+++ b/src/hbmenu.js
@@ -0,0 +1,250 @@
1 /*
2 ** Originally: Copyright © 2018 Warren Young
3 **
4 ** This program is free software; you can redistribute it and/or
5 ** modify it under the terms of the Simplified BSD License (also
6 ** known as the "2-Clause License" or "FreeBSD License".)
7 **
8 ** This program is distributed in the hope that it will be useful,
9 ** but without any warranty; without even the implied warranty of
10 ** merchantability or fitness for a particular purpose.
11 **
12 ** Contact: wyoung on the Fossil forum, https://fossil-scm.org/forum/
13 ** Modified by others.
14 **
15 *******************************************************************************
16 **
17 ** This file contains the JS code used to implement the expanding hamburger
18 ** menu on various skins.
19 **
20 ** This was original the "js.txt" file for the default skin. It was subsequently
21 ** moved into src/hbmenu.js so that it could be more easily reused by other skins
22 ** using the "builtin_request_js" TH1 command.
23 **
24 ** Operation:
25 **
26 ** This script request that the HTML contain two elements:
27 **
28 ** <a id="hbbtn"> <--- The hambdiger menu button
29 ** <nav id="hbdrop"> <--- Container for the hamburger menu
30 **
31 ** Bindings are made on hbbtn so that when it is clicked, the following
32 ** happens:
33 **
34 ** 1. An XHR is made to /sitemap?popup to fetch the HTML for the
35 ** popup menu.
36 **
37 ** 2. The HTML for the popup is inserted into hddrop.
38 **
39 ** 3. The hddrop container is made visible.
40 **
41 ** CSS rules are also needed to cause the hddrop to be initially invisible,
42 ** and to correctly style and position the hddrop container.
43 */
44 (function() {
45 var hbButton = document.getElementById("hbbtn");
46 if (!hbButton) return; // no hamburger button
47 if (!document.addEventListener) return; // Incompatible browser
48 var panel = document.getElementById("hbdrop");
49 if (!panel) return; // site admin might've nuked it
50 if (!panel.style) return; // shouldn't happen, but be sure
51 var panelBorder = panel.style.border;
52 var panelInitialized = false; // reset if browser window is resized
53 var panelResetBorderTimerID = 0; // used to cancel post-animation tasks
54
55 // Disable animation if this browser doesn't support CSS transitions.
56 //
57 // We need this ugly calling form for old browsers that don't allow
58 // panel.style.hasOwnProperty('transition'); catering to old browsers
59 // is the whole point here.
60 var animate = panel.style.transition !== null && (typeof(panel.style.transition) == "string");
61
62 // The duration of the animation can be overridden from the default skin
63 // header.txt by setting the "data-anim-ms" attribute of the panel.
64 var animMS = panel.getAttribute("data-anim-ms");
65 if (animMS) { // not null or empty string, parse it
66 animMS = parseInt(animMS);
67 if (isNaN(animMS) || animMS == 0)
68 animate = false; // disable animation if non-numeric or zero
69 else if (animMS < 0)
70 animMS = 400; // set default animation duration if negative
71 }
72 else // attribute is null or empty string, use default
73 animMS = 400;
74
75 // Calculate panel height despite its being hidden at call time.
76 // Based on https://stackoverflow.com/a/29047447/142454
77 var panelHeight; // computed on first panel display
78 function calculatePanelHeight() {
79
80 // Clear the max-height CSS property in case the panel size is recalculated
81 // after the browser window was resized.
82 panel.style.maxHeight = '';
83
84 // Get initial panel styles so we can restore them below.
85 var es = window.getComputedStyle(panel),
86 edis = es.display,
87 epos = es.position,
88 evis = es.visibility;
89
90 // Restyle the panel so we can measure its height while invisible.
91 panel.style.visibility = 'hidden';
92 panel.style.position = 'absolute';
93 panel.style.display = 'block';
94 panelHeight = panel.offsetHeight + 'px';
95
96 // Revert styles now that job is done.
97 panel.style.display = edis;
98 panel.style.position = epos;
99 panel.style.visibility = evis;
100 }
101
102 // Show the panel by changing the panel height, which kicks off the
103 // slide-open/closed transition set up in the XHR onload handler.
104 //
105 // Schedule the change for a near-future time in case this is the
106 // first call, where the div was initially invisible. If we were
107 // to change the panel's visibility and height at the same time
108 // instead, that would prevent the browser from seeing the height
109 // change as a state transition, so it'd skip the CSS transition:
110 //
111 // https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions#JavaScript_examples
112 function showPanel() {
113 // Cancel the timer to remove the panel border after the closing animation,
114 // otherwise double-clicking the hamburger button with the panel opened will
115 // remove the borders from the (closed and immediately reopened) panel.
116 if (panelResetBorderTimerID) {
117 clearTimeout(panelResetBorderTimerID);
118 panelResetBorderTimerID = 0;
119 }
120 if (animate) {
121 if (!panelInitialized) {
122 panelInitialized = true;
123 // Set up a CSS transition to animate the panel open and
124 // closed. Only needs to be done once per page load.
125 // Based on https://stackoverflow.com/a/29047447/142454
126 calculatePanelHeight();
127 panel.style.transition = 'max-height ' + animMS +
128 'ms ease-in-out';
129 panel.style.overflowY = 'hidden';
130 panel.style.maxHeight = '0';
131 }
132 setTimeout(function() {
133 panel.style.maxHeight = panelHeight;
134 panel.style.border = panelBorder;
135 }, 40); // 25ms is insufficient with Firefox 62
136 }
137 panel.style.display = 'block';
138 document.addEventListener('keydown',panelKeydown,/* useCapture == */true);
139 document.addEventListener('click',panelClick,false);
140 }
141
142 var panelKeydown = function(event) {
143 var key = event.which || event.keyCode;
144 if (key == 27) {
145 event.stopPropagation(); // ignore other keydown handlers
146 panelToggle(true);
147 }
148 };
149
150 var panelClick = function(event) {
151 if (!panel.contains(event.target)) {
152 // Call event.preventDefault() to have clicks outside the opened panel
153 // just close the panel, and swallow clicks on links or form elements.
154 //event.preventDefault();
155 panelToggle(true);
156 }
157 };
158
159 // Return true if the panel is showing.
160 function panelShowing() {
161 if (animate) {
162 return panel.style.maxHeight == panelHeight;
163 }
164 else {
165 return panel.style.display == 'block';
166 }
167 }
168
169 // Check if the specified HTML element has any child elements. Note that plain
170 // text nodes, comments, and any spaces (presentational or not) are ignored.
171 function hasChildren(element) {
172 var childElement = element.firstChild;
173 while (childElement) {
174 if (childElement.nodeType == 1) // Node.ELEMENT_NODE == 1
175 return true;
176 childElement = childElement.nextSibling;
177 }
178 return false;
179 }
180
181 // Reset the state of the panel to uninitialized if the browser window is
182 // resized, so the dimensions are recalculated the next time it's opened.
183 window.addEventListener('resize',function(event) {
184 panelInitialized = false;
185 },false);
186
187 // Click handler for the hamburger button.
188 hbButton.addEventListener('click',function(event) {
189 // Break the event handler chain, or the handler for document → click
190 // (about to be installed) may already be triggered by the current event.
191 event.stopPropagation();
192 event.preventDefault(); // prevent browser from acting on <a> click
193 panelToggle(false);
194 },false);
195
196 function panelToggle(suppressAnimation) {
197 if (panelShowing()) {
198 document.removeEventListener('keydown',panelKeydown,/* useCapture == */true);
199 document.removeEventListener('click',panelClick,false);
200 // Transition back to hidden state.
201 if (animate) {
202 if (suppressAnimation) {
203 var transition = panel.style.transition;
204 panel.style.transition = '';
205 panel.style.maxHeight = '0';
206 panel.style.border = 'none';
207 setTimeout(function() {
208 // Make sure CSS transition won't take effect now, so restore it
209 // asynchronously. Outer variable 'transition' still valid here.
210 panel.style.transition = transition;
211 }, 40); // 25ms is insufficient with Firefox 62
212 }
213 else {
214 panel.style.maxHeight = '0';
215 panelResetBorderTimerID = setTimeout(function() {
216 // Browsers show a 1px high border line when maxHeight == 0,
217 // our "hidden" state, so hide the borders in that state, too.
218 panel.style.border = 'none';
219 panelResetBorderTimerID = 0; // clear ID of completed timer
220 }, animMS);
221 }
222 }
223 else {
224 panel.style.display = 'none';
225 }
226 }
227 else {
228 if (!hasChildren(panel)) {
229 // Only get the sitemap once per page load: it isn't likely to
230 // change on us.
231 var xhr = new XMLHttpRequest();
232 xhr.onload = function() {
233 var doc = xhr.responseXML;
234 if (doc) {
235 var sm = doc.querySelector("ul#sitemap");
236 if (sm && xhr.status == 200) {
237 // Got sitemap. Insert it into the drop-down panel.
238 panel.innerHTML = sm.outerHTML;
239 // Display the panel
240 showPanel();
241 }
242 }
243 // else, can't parse response as HTML or XML
244 }
245 xhr.open("GET xhr.open("GET", url);
246 xhr.responseType = "document";
247 xhr.send();
248 }
249 else {
250 showPanel(); // just show what we built abo
+1 -1
--- src/main.mk
+++ src/main.mk
@@ -190,11 +190,10 @@
190190
$(SRCDIR)/../skins/bootstrap/header.txt \
191191
$(SRCDIR)/../skins/default/css.txt \
192192
$(SRCDIR)/../skins/default/details.txt \
193193
$(SRCDIR)/../skins/default/footer.txt \
194194
$(SRCDIR)/../skins/default/header.txt \
195
- $(SRCDIR)/../skins/default/js.txt \
196195
$(SRCDIR)/../skins/eagle/css.txt \
197196
$(SRCDIR)/../skins/eagle/details.txt \
198197
$(SRCDIR)/../skins/eagle/footer.txt \
199198
$(SRCDIR)/../skins/eagle/header.txt \
200199
$(SRCDIR)/../skins/enhanced1/css.txt \
@@ -246,10 +245,11 @@
246245
$(SRCDIR)/fossil.popupwidget.js \
247246
$(SRCDIR)/fossil.storage.js \
248247
$(SRCDIR)/fossil.tabs.js \
249248
$(SRCDIR)/fossil.wikiedit-wysiwyg.js \
250249
$(SRCDIR)/graph.js \
250
+ $(SRCDIR)/hbmenu.js \
251251
$(SRCDIR)/href.js \
252252
$(SRCDIR)/login.js \
253253
$(SRCDIR)/markdown.md \
254254
$(SRCDIR)/menu.js \
255255
$(SRCDIR)/sbsdiff.js \
256256
--- src/main.mk
+++ src/main.mk
@@ -190,11 +190,10 @@
190 $(SRCDIR)/../skins/bootstrap/header.txt \
191 $(SRCDIR)/../skins/default/css.txt \
192 $(SRCDIR)/../skins/default/details.txt \
193 $(SRCDIR)/../skins/default/footer.txt \
194 $(SRCDIR)/../skins/default/header.txt \
195 $(SRCDIR)/../skins/default/js.txt \
196 $(SRCDIR)/../skins/eagle/css.txt \
197 $(SRCDIR)/../skins/eagle/details.txt \
198 $(SRCDIR)/../skins/eagle/footer.txt \
199 $(SRCDIR)/../skins/eagle/header.txt \
200 $(SRCDIR)/../skins/enhanced1/css.txt \
@@ -246,10 +245,11 @@
246 $(SRCDIR)/fossil.popupwidget.js \
247 $(SRCDIR)/fossil.storage.js \
248 $(SRCDIR)/fossil.tabs.js \
249 $(SRCDIR)/fossil.wikiedit-wysiwyg.js \
250 $(SRCDIR)/graph.js \
 
251 $(SRCDIR)/href.js \
252 $(SRCDIR)/login.js \
253 $(SRCDIR)/markdown.md \
254 $(SRCDIR)/menu.js \
255 $(SRCDIR)/sbsdiff.js \
256
--- src/main.mk
+++ src/main.mk
@@ -190,11 +190,10 @@
190 $(SRCDIR)/../skins/bootstrap/header.txt \
191 $(SRCDIR)/../skins/default/css.txt \
192 $(SRCDIR)/../skins/default/details.txt \
193 $(SRCDIR)/../skins/default/footer.txt \
194 $(SRCDIR)/../skins/default/header.txt \
 
195 $(SRCDIR)/../skins/eagle/css.txt \
196 $(SRCDIR)/../skins/eagle/details.txt \
197 $(SRCDIR)/../skins/eagle/footer.txt \
198 $(SRCDIR)/../skins/eagle/header.txt \
199 $(SRCDIR)/../skins/enhanced1/css.txt \
@@ -246,10 +245,11 @@
245 $(SRCDIR)/fossil.popupwidget.js \
246 $(SRCDIR)/fossil.storage.js \
247 $(SRCDIR)/fossil.tabs.js \
248 $(SRCDIR)/fossil.wikiedit-wysiwyg.js \
249 $(SRCDIR)/graph.js \
250 $(SRCDIR)/hbmenu.js \
251 $(SRCDIR)/href.js \
252 $(SRCDIR)/login.js \
253 $(SRCDIR)/markdown.md \
254 $(SRCDIR)/menu.js \
255 $(SRCDIR)/sbsdiff.js \
256
--- src/th_main.c
+++ src/th_main.c
@@ -1492,10 +1492,15 @@
14921492
/*
14931493
** TH1 command: styleScript ?BUILTIN-FILENAME?
14941494
**
14951495
** Render the js.txt file from the current skin. Or, if an argument
14961496
** is supplied, render the built-in filename given.
1497
+**
1498
+** By "rendering" we mean that the script is loaded and run through
1499
+** TH1 to expand variables and process <th1>...</th1> script. Contrast
1500
+** with the "builtin_request_js BUILTIN-FILENAME" command which just
1501
+** loads the file as-is without interpretation.
14971502
*/
14981503
static int styleScriptCmd(
14991504
Th_Interp *interp,
15001505
void *p,
15011506
int argc,
@@ -1520,10 +1525,31 @@
15201525
Th_SetResult(interp, "repository unavailable", -1);
15211526
return TH_ERROR;
15221527
}
15231528
}
15241529
1530
+/*
1531
+** TH1 command: builtin_request_js NAME
1532
+**
1533
+** Request that the built-in javascript file called NAME be added to the
1534
+** end of the generated page.
1535
+**
1536
+** See also: styleScript
1537
+*/
1538
+static int builtinRequestJsCmd(
1539
+ Th_Interp *interp,
1540
+ void *p,
1541
+ int argc,
1542
+ const char **argv,
1543
+ int *argl
1544
+){
1545
+ if( argc!=2 ){
1546
+ return Th_WrongNumArgs(interp, "builtin_request_js NAME");
1547
+ }
1548
+ builtin_request_js(argv[1]);
1549
+ return TH_OK;
1550
+}
15251551
15261552
/*
15271553
** TH1 command: artifact ID ?FILENAME?
15281554
**
15291555
** Attempts to locate the specified artifact and return its contents. An
@@ -2237,10 +2263,11 @@
22372263
void *pContext;
22382264
} aCommand[] = {
22392265
{"anoncap", hascapCmd, (void*)&anonFlag},
22402266
{"anycap", anycapCmd, 0},
22412267
{"artifact", artifactCmd, 0},
2268
+ {"builtin_request_js", builtinRequestJsCmd, 0},
22422269
{"captureTh1", captureTh1Cmd, 0},
22432270
{"cgiHeaderLine", cgiHeaderLineCmd, 0},
22442271
{"checkout", checkoutCmd, 0},
22452272
{"combobox", comboboxCmd, 0},
22462273
{"copybtn", copybtnCmd, 0},
22472274
--- src/th_main.c
+++ src/th_main.c
@@ -1492,10 +1492,15 @@
1492 /*
1493 ** TH1 command: styleScript ?BUILTIN-FILENAME?
1494 **
1495 ** Render the js.txt file from the current skin. Or, if an argument
1496 ** is supplied, render the built-in filename given.
 
 
 
 
 
1497 */
1498 static int styleScriptCmd(
1499 Th_Interp *interp,
1500 void *p,
1501 int argc,
@@ -1520,10 +1525,31 @@
1520 Th_SetResult(interp, "repository unavailable", -1);
1521 return TH_ERROR;
1522 }
1523 }
1524
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1525
1526 /*
1527 ** TH1 command: artifact ID ?FILENAME?
1528 **
1529 ** Attempts to locate the specified artifact and return its contents. An
@@ -2237,10 +2263,11 @@
2237 void *pContext;
2238 } aCommand[] = {
2239 {"anoncap", hascapCmd, (void*)&anonFlag},
2240 {"anycap", anycapCmd, 0},
2241 {"artifact", artifactCmd, 0},
 
2242 {"captureTh1", captureTh1Cmd, 0},
2243 {"cgiHeaderLine", cgiHeaderLineCmd, 0},
2244 {"checkout", checkoutCmd, 0},
2245 {"combobox", comboboxCmd, 0},
2246 {"copybtn", copybtnCmd, 0},
2247
--- src/th_main.c
+++ src/th_main.c
@@ -1492,10 +1492,15 @@
1492 /*
1493 ** TH1 command: styleScript ?BUILTIN-FILENAME?
1494 **
1495 ** Render the js.txt file from the current skin. Or, if an argument
1496 ** is supplied, render the built-in filename given.
1497 **
1498 ** By "rendering" we mean that the script is loaded and run through
1499 ** TH1 to expand variables and process <th1>...</th1> script. Contrast
1500 ** with the "builtin_request_js BUILTIN-FILENAME" command which just
1501 ** loads the file as-is without interpretation.
1502 */
1503 static int styleScriptCmd(
1504 Th_Interp *interp,
1505 void *p,
1506 int argc,
@@ -1520,10 +1525,31 @@
1525 Th_SetResult(interp, "repository unavailable", -1);
1526 return TH_ERROR;
1527 }
1528 }
1529
1530 /*
1531 ** TH1 command: builtin_request_js NAME
1532 **
1533 ** Request that the built-in javascript file called NAME be added to the
1534 ** end of the generated page.
1535 **
1536 ** See also: styleScript
1537 */
1538 static int builtinRequestJsCmd(
1539 Th_Interp *interp,
1540 void *p,
1541 int argc,
1542 const char **argv,
1543 int *argl
1544 ){
1545 if( argc!=2 ){
1546 return Th_WrongNumArgs(interp, "builtin_request_js NAME");
1547 }
1548 builtin_request_js(argv[1]);
1549 return TH_OK;
1550 }
1551
1552 /*
1553 ** TH1 command: artifact ID ?FILENAME?
1554 **
1555 ** Attempts to locate the specified artifact and return its contents. An
@@ -2237,10 +2263,11 @@
2263 void *pContext;
2264 } aCommand[] = {
2265 {"anoncap", hascapCmd, (void*)&anonFlag},
2266 {"anycap", anycapCmd, 0},
2267 {"artifact", artifactCmd, 0},
2268 {"builtin_request_js", builtinRequestJsCmd, 0},
2269 {"captureTh1", captureTh1Cmd, 0},
2270 {"cgiHeaderLine", cgiHeaderLineCmd, 0},
2271 {"checkout", checkoutCmd, 0},
2272 {"combobox", comboboxCmd, 0},
2273 {"copybtn", copybtnCmd, 0},
2274
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -602,11 +602,10 @@
602602
$(SRCDIR)/../skins/bootstrap/header.txt \
603603
$(SRCDIR)/../skins/default/css.txt \
604604
$(SRCDIR)/../skins/default/details.txt \
605605
$(SRCDIR)/../skins/default/footer.txt \
606606
$(SRCDIR)/../skins/default/header.txt \
607
- $(SRCDIR)/../skins/default/js.txt \
608607
$(SRCDIR)/../skins/eagle/css.txt \
609608
$(SRCDIR)/../skins/eagle/details.txt \
610609
$(SRCDIR)/../skins/eagle/footer.txt \
611610
$(SRCDIR)/../skins/eagle/header.txt \
612611
$(SRCDIR)/../skins/enhanced1/css.txt \
@@ -658,10 +657,11 @@
658657
$(SRCDIR)/fossil.popupwidget.js \
659658
$(SRCDIR)/fossil.storage.js \
660659
$(SRCDIR)/fossil.tabs.js \
661660
$(SRCDIR)/fossil.wikiedit-wysiwyg.js \
662661
$(SRCDIR)/graph.js \
662
+ $(SRCDIR)/hbmenu.js \
663663
$(SRCDIR)/href.js \
664664
$(SRCDIR)/login.js \
665665
$(SRCDIR)/markdown.md \
666666
$(SRCDIR)/menu.js \
667667
$(SRCDIR)/sbsdiff.js \
668668
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -602,11 +602,10 @@
602 $(SRCDIR)/../skins/bootstrap/header.txt \
603 $(SRCDIR)/../skins/default/css.txt \
604 $(SRCDIR)/../skins/default/details.txt \
605 $(SRCDIR)/../skins/default/footer.txt \
606 $(SRCDIR)/../skins/default/header.txt \
607 $(SRCDIR)/../skins/default/js.txt \
608 $(SRCDIR)/../skins/eagle/css.txt \
609 $(SRCDIR)/../skins/eagle/details.txt \
610 $(SRCDIR)/../skins/eagle/footer.txt \
611 $(SRCDIR)/../skins/eagle/header.txt \
612 $(SRCDIR)/../skins/enhanced1/css.txt \
@@ -658,10 +657,11 @@
658 $(SRCDIR)/fossil.popupwidget.js \
659 $(SRCDIR)/fossil.storage.js \
660 $(SRCDIR)/fossil.tabs.js \
661 $(SRCDIR)/fossil.wikiedit-wysiwyg.js \
662 $(SRCDIR)/graph.js \
 
663 $(SRCDIR)/href.js \
664 $(SRCDIR)/login.js \
665 $(SRCDIR)/markdown.md \
666 $(SRCDIR)/menu.js \
667 $(SRCDIR)/sbsdiff.js \
668
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -602,11 +602,10 @@
602 $(SRCDIR)/../skins/bootstrap/header.txt \
603 $(SRCDIR)/../skins/default/css.txt \
604 $(SRCDIR)/../skins/default/details.txt \
605 $(SRCDIR)/../skins/default/footer.txt \
606 $(SRCDIR)/../skins/default/header.txt \
 
607 $(SRCDIR)/../skins/eagle/css.txt \
608 $(SRCDIR)/../skins/eagle/details.txt \
609 $(SRCDIR)/../skins/eagle/footer.txt \
610 $(SRCDIR)/../skins/eagle/header.txt \
611 $(SRCDIR)/../skins/enhanced1/css.txt \
@@ -658,10 +657,11 @@
657 $(SRCDIR)/fossil.popupwidget.js \
658 $(SRCDIR)/fossil.storage.js \
659 $(SRCDIR)/fossil.tabs.js \
660 $(SRCDIR)/fossil.wikiedit-wysiwyg.js \
661 $(SRCDIR)/graph.js \
662 $(SRCDIR)/hbmenu.js \
663 $(SRCDIR)/href.js \
664 $(SRCDIR)/login.js \
665 $(SRCDIR)/markdown.md \
666 $(SRCDIR)/menu.js \
667 $(SRCDIR)/sbsdiff.js \
668
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -523,11 +523,10 @@
523523
"$(SRCDIR)\..\skins\bootstrap\header.txt" \
524524
"$(SRCDIR)\..\skins\default\css.txt" \
525525
"$(SRCDIR)\..\skins\default\details.txt" \
526526
"$(SRCDIR)\..\skins\default\footer.txt" \
527527
"$(SRCDIR)\..\skins\default\header.txt" \
528
- "$(SRCDIR)\..\skins\default\js.txt" \
529528
"$(SRCDIR)\..\skins\eagle\css.txt" \
530529
"$(SRCDIR)\..\skins\eagle\details.txt" \
531530
"$(SRCDIR)\..\skins\eagle\footer.txt" \
532531
"$(SRCDIR)\..\skins\eagle\header.txt" \
533532
"$(SRCDIR)\..\skins\enhanced1\css.txt" \
@@ -579,10 +578,11 @@
579578
"$(SRCDIR)\fossil.popupwidget.js" \
580579
"$(SRCDIR)\fossil.storage.js" \
581580
"$(SRCDIR)\fossil.tabs.js" \
582581
"$(SRCDIR)\fossil.wikiedit-wysiwyg.js" \
583582
"$(SRCDIR)\graph.js" \
583
+ "$(SRCDIR)\hbmenu.js" \
584584
"$(SRCDIR)\href.js" \
585585
"$(SRCDIR)\login.js" \
586586
"$(SRCDIR)\markdown.md" \
587587
"$(SRCDIR)\menu.js" \
588588
"$(SRCDIR)\sbsdiff.js" \
@@ -1136,11 +1136,10 @@
11361136
echo "$(SRCDIR)\../skins/bootstrap/header.txt" >> $@
11371137
echo "$(SRCDIR)\../skins/default/css.txt" >> $@
11381138
echo "$(SRCDIR)\../skins/default/details.txt" >> $@
11391139
echo "$(SRCDIR)\../skins/default/footer.txt" >> $@
11401140
echo "$(SRCDIR)\../skins/default/header.txt" >> $@
1141
- echo "$(SRCDIR)\../skins/default/js.txt" >> $@
11421141
echo "$(SRCDIR)\../skins/eagle/css.txt" >> $@
11431142
echo "$(SRCDIR)\../skins/eagle/details.txt" >> $@
11441143
echo "$(SRCDIR)\../skins/eagle/footer.txt" >> $@
11451144
echo "$(SRCDIR)\../skins/eagle/header.txt" >> $@
11461145
echo "$(SRCDIR)\../skins/enhanced1/css.txt" >> $@
@@ -1192,10 +1191,11 @@
11921191
echo "$(SRCDIR)\fossil.popupwidget.js" >> $@
11931192
echo "$(SRCDIR)\fossil.storage.js" >> $@
11941193
echo "$(SRCDIR)\fossil.tabs.js" >> $@
11951194
echo "$(SRCDIR)\fossil.wikiedit-wysiwyg.js" >> $@
11961195
echo "$(SRCDIR)\graph.js" >> $@
1196
+ echo "$(SRCDIR)\hbmenu.js" >> $@
11971197
echo "$(SRCDIR)\href.js" >> $@
11981198
echo "$(SRCDIR)\login.js" >> $@
11991199
echo "$(SRCDIR)\markdown.md" >> $@
12001200
echo "$(SRCDIR)\menu.js" >> $@
12011201
echo "$(SRCDIR)\sbsdiff.js" >> $@
12021202
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -523,11 +523,10 @@
523 "$(SRCDIR)\..\skins\bootstrap\header.txt" \
524 "$(SRCDIR)\..\skins\default\css.txt" \
525 "$(SRCDIR)\..\skins\default\details.txt" \
526 "$(SRCDIR)\..\skins\default\footer.txt" \
527 "$(SRCDIR)\..\skins\default\header.txt" \
528 "$(SRCDIR)\..\skins\default\js.txt" \
529 "$(SRCDIR)\..\skins\eagle\css.txt" \
530 "$(SRCDIR)\..\skins\eagle\details.txt" \
531 "$(SRCDIR)\..\skins\eagle\footer.txt" \
532 "$(SRCDIR)\..\skins\eagle\header.txt" \
533 "$(SRCDIR)\..\skins\enhanced1\css.txt" \
@@ -579,10 +578,11 @@
579 "$(SRCDIR)\fossil.popupwidget.js" \
580 "$(SRCDIR)\fossil.storage.js" \
581 "$(SRCDIR)\fossil.tabs.js" \
582 "$(SRCDIR)\fossil.wikiedit-wysiwyg.js" \
583 "$(SRCDIR)\graph.js" \
 
584 "$(SRCDIR)\href.js" \
585 "$(SRCDIR)\login.js" \
586 "$(SRCDIR)\markdown.md" \
587 "$(SRCDIR)\menu.js" \
588 "$(SRCDIR)\sbsdiff.js" \
@@ -1136,11 +1136,10 @@
1136 echo "$(SRCDIR)\../skins/bootstrap/header.txt" >> $@
1137 echo "$(SRCDIR)\../skins/default/css.txt" >> $@
1138 echo "$(SRCDIR)\../skins/default/details.txt" >> $@
1139 echo "$(SRCDIR)\../skins/default/footer.txt" >> $@
1140 echo "$(SRCDIR)\../skins/default/header.txt" >> $@
1141 echo "$(SRCDIR)\../skins/default/js.txt" >> $@
1142 echo "$(SRCDIR)\../skins/eagle/css.txt" >> $@
1143 echo "$(SRCDIR)\../skins/eagle/details.txt" >> $@
1144 echo "$(SRCDIR)\../skins/eagle/footer.txt" >> $@
1145 echo "$(SRCDIR)\../skins/eagle/header.txt" >> $@
1146 echo "$(SRCDIR)\../skins/enhanced1/css.txt" >> $@
@@ -1192,10 +1191,11 @@
1192 echo "$(SRCDIR)\fossil.popupwidget.js" >> $@
1193 echo "$(SRCDIR)\fossil.storage.js" >> $@
1194 echo "$(SRCDIR)\fossil.tabs.js" >> $@
1195 echo "$(SRCDIR)\fossil.wikiedit-wysiwyg.js" >> $@
1196 echo "$(SRCDIR)\graph.js" >> $@
 
1197 echo "$(SRCDIR)\href.js" >> $@
1198 echo "$(SRCDIR)\login.js" >> $@
1199 echo "$(SRCDIR)\markdown.md" >> $@
1200 echo "$(SRCDIR)\menu.js" >> $@
1201 echo "$(SRCDIR)\sbsdiff.js" >> $@
1202
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -523,11 +523,10 @@
523 "$(SRCDIR)\..\skins\bootstrap\header.txt" \
524 "$(SRCDIR)\..\skins\default\css.txt" \
525 "$(SRCDIR)\..\skins\default\details.txt" \
526 "$(SRCDIR)\..\skins\default\footer.txt" \
527 "$(SRCDIR)\..\skins\default\header.txt" \
 
528 "$(SRCDIR)\..\skins\eagle\css.txt" \
529 "$(SRCDIR)\..\skins\eagle\details.txt" \
530 "$(SRCDIR)\..\skins\eagle\footer.txt" \
531 "$(SRCDIR)\..\skins\eagle\header.txt" \
532 "$(SRCDIR)\..\skins\enhanced1\css.txt" \
@@ -579,10 +578,11 @@
578 "$(SRCDIR)\fossil.popupwidget.js" \
579 "$(SRCDIR)\fossil.storage.js" \
580 "$(SRCDIR)\fossil.tabs.js" \
581 "$(SRCDIR)\fossil.wikiedit-wysiwyg.js" \
582 "$(SRCDIR)\graph.js" \
583 "$(SRCDIR)\hbmenu.js" \
584 "$(SRCDIR)\href.js" \
585 "$(SRCDIR)\login.js" \
586 "$(SRCDIR)\markdown.md" \
587 "$(SRCDIR)\menu.js" \
588 "$(SRCDIR)\sbsdiff.js" \
@@ -1136,11 +1136,10 @@
1136 echo "$(SRCDIR)\../skins/bootstrap/header.txt" >> $@
1137 echo "$(SRCDIR)\../skins/default/css.txt" >> $@
1138 echo "$(SRCDIR)\../skins/default/details.txt" >> $@
1139 echo "$(SRCDIR)\../skins/default/footer.txt" >> $@
1140 echo "$(SRCDIR)\../skins/default/header.txt" >> $@
 
1141 echo "$(SRCDIR)\../skins/eagle/css.txt" >> $@
1142 echo "$(SRCDIR)\../skins/eagle/details.txt" >> $@
1143 echo "$(SRCDIR)\../skins/eagle/footer.txt" >> $@
1144 echo "$(SRCDIR)\../skins/eagle/header.txt" >> $@
1145 echo "$(SRCDIR)\../skins/enhanced1/css.txt" >> $@
@@ -1192,10 +1191,11 @@
1191 echo "$(SRCDIR)\fossil.popupwidget.js" >> $@
1192 echo "$(SRCDIR)\fossil.storage.js" >> $@
1193 echo "$(SRCDIR)\fossil.tabs.js" >> $@
1194 echo "$(SRCDIR)\fossil.wikiedit-wysiwyg.js" >> $@
1195 echo "$(SRCDIR)\graph.js" >> $@
1196 echo "$(SRCDIR)\hbmenu.js" >> $@
1197 echo "$(SRCDIR)\href.js" >> $@
1198 echo "$(SRCDIR)\login.js" >> $@
1199 echo "$(SRCDIR)\markdown.md" >> $@
1200 echo "$(SRCDIR)\menu.js" >> $@
1201 echo "$(SRCDIR)\sbsdiff.js" >> $@
1202

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button