Fossil SCM

Enhancements to the hamburger menu mechanism.

drh 2019-01-02 15:31 trunk merge
Commit 724a9b8f3c8d82d015f2073e9879fa70b4ec753eab1e846a9c8ab0aa380cf6dd
--- skins/default/header.txt
+++ skins/default/header.txt
@@ -17,11 +17,11 @@
1717
html "<a href='$home$url' class='active $cls'>$name</a>\n"
1818
} else {
1919
html "<a href='$home$url' class='$cls'>$name</a>\n"
2020
}
2121
}
22
-html "<a href='#'>&#9776;</a>"
22
+html "<a id='hbbtn' href='#'>&#9776;</a>"
2323
menulink $index_page Home {}
2424
if {[anycap jor]} {
2525
menulink /timeline Timeline {}
2626
}
2727
if {[hascap oh]} {
2828
--- skins/default/header.txt
+++ skins/default/header.txt
@@ -17,11 +17,11 @@
17 html "<a href='$home$url' class='active $cls'>$name</a>\n"
18 } else {
19 html "<a href='$home$url' class='$cls'>$name</a>\n"
20 }
21 }
22 html "<a href='#'>&#9776;</a>"
23 menulink $index_page Home {}
24 if {[anycap jor]} {
25 menulink /timeline Timeline {}
26 }
27 if {[hascap oh]} {
28
--- skins/default/header.txt
+++ skins/default/header.txt
@@ -17,11 +17,11 @@
17 html "<a href='$home$url' class='active $cls'>$name</a>\n"
18 } else {
19 html "<a href='$home$url' class='$cls'>$name</a>\n"
20 }
21 }
22 html "<a id='hbbtn' href='#'>&#9776;</a>"
23 menulink $index_page Home {}
24 if {[anycap jor]} {
25 menulink /timeline Timeline {}
26 }
27 if {[hascap oh]} {
28
--- skins/default/js.txt
+++ skins/default/js.txt
@@ -16,27 +16,53 @@
1616
** This file contains the JS code specific to the Fossil default skin.
1717
** Currently, the only thing this does is handle clicks on its hamburger
1818
** menu button.
1919
*/
2020
(function() {
21
+ var hbButton = document.getElementById("hbbtn");
22
+ if (!hbButton) return; // no hamburger button
23
+ if (!document.addEventListener) {
24
+ // Turn the button into a link to the sitemap for incompatible browsers.
25
+ hbButton.href = "$home/sitemap";
26
+ return;
27
+ }
2128
var panel = document.getElementById("hbdrop");
2229
if (!panel) return; // site admin might've nuked it
2330
if (!panel.style) return; // shouldn't happen, but be sure
2431
var panelBorder = panel.style.border;
32
+ var panelInitialized = false; // reset if browser window is resized
33
+ var panelResetBorderTimerID = 0; // used to cancel post-animation tasks
2534
2635
// Disable animation if this browser doesn't support CSS transitions.
2736
//
2837
// We need this ugly calling form for old browsers that don't allow
2938
// panel.style.hasOwnProperty('transition'); catering to old browsers
3039
// is the whole point here.
3140
var animate = panel.style.transition !== null && (typeof(panel.style.transition) == "string");
32
- var animMS = 400;
41
+
42
+ // The duration of the animation can be overridden from the default skin
43
+ // header.txt by setting the "data-anim-ms" attribute of the panel.
44
+ var animMS = panel.getAttribute("data-anim-ms");
45
+ if (animMS) { // not null or empty string, parse it
46
+ animMS = parseInt(animMS);
47
+ if (isNaN(animMS) || animMS == 0)
48
+ animate = false; // disable animation if non-numeric or zero
49
+ else if (animMS < 0)
50
+ animMS = 400; // set default animation duration if negative
51
+ }
52
+ else // attribute is null or empty string, use default
53
+ animMS = 400;
3354
3455
// Calculate panel height despite its being hidden at call time.
3556
// Based on https://stackoverflow.com/a/29047447/142454
36
- var panelHeight; // computed on sitemap load
57
+ var panelHeight; // computed on first panel display
3758
function calculatePanelHeight() {
59
+
60
+ // Clear the max-height CSS property in case the panel size is recalculated
61
+ // after the browser window was resized.
62
+ panel.style.maxHeight = '';
63
+
3864
// Get initial panel styles so we can restore them below.
3965
var es = window.getComputedStyle(panel),
4066
edis = es.display,
4167
epos = es.position,
4268
evis = es.visibility;
@@ -62,20 +88,55 @@
6288
// instead, that would prevent the browser from seeing the height
6389
// change as a state transition, so it'd skip the CSS transition:
6490
//
6591
// https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions#JavaScript_examples
6692
function showPanel() {
93
+ // Cancel the timer to remove the panel border after the closing animation,
94
+ // otherwise double-clicking the hamburger button with the panel opened will
95
+ // remove the borders from the (closed and immediately reopened) panel.
96
+ if (panelResetBorderTimerID) {
97
+ clearTimeout(panelResetBorderTimerID);
98
+ panelResetBorderTimerID = 0;
99
+ }
67100
if (animate) {
101
+ if (!panelInitialized) {
102
+ panelInitialized = true;
103
+ // Set up a CSS transition to animate the panel open and
104
+ // closed. Only needs to be done once per page load.
105
+ // Based on https://stackoverflow.com/a/29047447/142454
106
+ calculatePanelHeight();
107
+ panel.style.transition = 'max-height ' + animMS +
108
+ 'ms ease-in-out';
109
+ panel.style.overflowY = 'hidden';
110
+ panel.style.maxHeight = '0';
111
+ }
68112
setTimeout(function() {
69113
panel.style.maxHeight = panelHeight;
70114
panel.style.border = panelBorder;
71115
}, 40); // 25ms is insufficient with Firefox 62
72116
}
73
- else {
74
- panel.style.display = 'block';
75
- }
117
+ panel.style.display = 'block';
118
+ document.addEventListener('keydown',panelKeydown,/* useCapture == */true);
119
+ document.addEventListener('click',panelClick,false);
76120
}
121
+
122
+ var panelKeydown = function(event) {
123
+ var key = event.which || event.keyCode;
124
+ if (key == 27) {
125
+ event.stopPropagation(); // ignore other keydown handlers
126
+ panelToggle(true);
127
+ }
128
+ };
129
+
130
+ var panelClick = function(event) {
131
+ if (!panel.contains(event.target)) {
132
+ // Call event.preventDefault() to have clicks outside the opened panel
133
+ // just close the panel, and swallow clicks on links or form elements.
134
+ //event.preventDefault();
135
+ panelToggle(true);
136
+ }
137
+ };
77138
78139
// Return true if the panel is showing.
79140
function panelShowing() {
80141
if (animate) {
81142
return panel.style.maxHeight == panelHeight;
@@ -82,63 +143,92 @@
82143
}
83144
else {
84145
return panel.style.display == 'block';
85146
}
86147
}
148
+
149
+ // Check if the specified HTML element has any child elements. Note that plain
150
+ // text nodes, comments, and any spaces (presentational or not) are ignored.
151
+ function hasChildren(element) {
152
+ var childElement = element.firstChild;
153
+ while (childElement) {
154
+ if (childElement.nodeType == 1) // Node.ELEMENT_NODE == 1
155
+ return true;
156
+ childElement = childElement.nextSibling;
157
+ }
158
+ return false;
159
+ }
160
+
161
+ // Reset the state of the panel to uninitialized if the browser window is
162
+ // resized, so the dimensions are recalculated the next time it's opened.
163
+ window.addEventListener('resize',function(event) {
164
+ panelInitialized = false;
165
+ },false);
87166
88167
// Click handler for the hamburger button.
89
- var needSitemapHTML = true;
90
- document.querySelector("div.mainmenu > a").onclick = function() {
168
+ hbButton.addEventListener('click',function(event) {
169
+ // Break the event handler chain, or the handler for document → click
170
+ // (about to be installed) may already be triggered by the current event.
171
+ event.stopPropagation();
172
+ event.preventDefault(); // prevent browser from acting on <a> click
173
+ panelToggle(false);
174
+ },false);
175
+
176
+ function panelToggle(suppressAnimation) {
91177
if (panelShowing()) {
178
+ document.removeEventListener('keydown',panelKeydown,/* useCapture == */true);
179
+ document.removeEventListener('click',panelClick,false);
92180
// Transition back to hidden state.
93181
if (animate) {
94
- panel.style.maxHeight = '0';
95
- setTimeout(function() {
96
- // Browsers show a 1px high border line when maxHeight == 0,
97
- // our "hidden" state, so hide the borders in that state, too.
182
+ if (suppressAnimation) {
183
+ var transition = panel.style.transition;
184
+ panel.style.transition = '';
185
+ panel.style.maxHeight = '0';
98186
panel.style.border = 'none';
99
- }, animMS);
187
+ setTimeout(function() {
188
+ // Make sure CSS transition won't take effect now, so restore it
189
+ // asynchronously. Outer variable 'transition' still valid here.
190
+ panel.style.transition = transition;
191
+ }, 40); // 25ms is insufficient with Firefox 62
192
+ }
193
+ else {
194
+ panel.style.maxHeight = '0';
195
+ panelResetBorderTimerID = setTimeout(function() {
196
+ // Browsers show a 1px high border line when maxHeight == 0,
197
+ // our "hidden" state, so hide the borders in that state, too.
198
+ panel.style.border = 'none';
199
+ panelResetBorderTimerID = 0; // clear ID of completed timer
200
+ }, animMS);
201
+ }
100202
}
101203
else {
102204
panel.style.display = 'none';
103205
}
104206
}
105
- else if (needSitemapHTML) {
106
- // Only get it once per page load: it isn't likely to
107
- // change on us.
108
- var xhr = new XMLHttpRequest();
109
- xhr.onload = function() {
110
- var doc = xhr.responseXML;
111
- if (doc) {
112
- var sm = doc.querySelector("ul#sitemap");
113
- if (sm && xhr.status == 200) {
114
- // Got sitemap. Insert it into the drop-down panel.
115
- needSitemapHTML = false;
116
- panel.innerHTML = sm.outerHTML;
117
-
118
- // Display the panel
119
- if (animate) {
120
- // Set up a CSS transition to animate the panel open and
121
- // closed. Only needs to be done once per page load.
122
- // Based on https://stackoverflow.com/a/29047447/142454
123
- calculatePanelHeight();
124
- panel.style.transition = 'max-height ' + animMS +
125
- 'ms ease-in-out';
126
- panel.style.overflowY = 'hidden';
127
- panel.style.maxHeight = '0';
207
+ else {
208
+ if (!hasChildren(panel)) {
209
+ // Only get the sitemap once per page load: it isn't likely to
210
+ // change on us.
211
+ var xhr = new XMLHttpRequest();
212
+ xhr.onload = function() {
213
+ var doc = xhr.responseXML;
214
+ if (doc) {
215
+ var sm = doc.querySelector("ul#sitemap");
216
+ if (sm && xhr.status == 200) {
217
+ // Got sitemap. Insert it into the drop-down panel.
218
+ panel.innerHTML = sm.outerHTML;
219
+ // Display the panel
128220
showPanel();
129221
}
130
- panel.style.display = 'block';
131
- }
132
- }
133
- // else, can't parse response as HTML or XML
134
- }
135
- xhr.open("GET", "$home/sitemap?popup"); // note the TH1 substitution!
136
- xhr.responseType = "document";
137
- xhr.send();
138
- }
139
- else {
140
- showPanel(); // just show what we built above
141
- }
142
- return false; // prevent browser from acting on <a> click
222
+ }
223
+ // else, can't parse response as HTML or XML
224
+ }
225
+ xhr.open("GET", "$home/sitemap?popup"); // note the TH1 substitution!
226
+ xhr.responseType = "document";
227
+ xhr.send();
228
+ }
229
+ else {
230
+ showPanel(); // just show what we built above
231
+ }
232
+ }
143233
}
144234
})();
145235
--- skins/default/js.txt
+++ skins/default/js.txt
@@ -16,27 +16,53 @@
16 ** This file contains the JS code specific to the Fossil default skin.
17 ** Currently, the only thing this does is handle clicks on its hamburger
18 ** menu button.
19 */
20 (function() {
 
 
 
 
 
 
 
21 var panel = document.getElementById("hbdrop");
22 if (!panel) return; // site admin might've nuked it
23 if (!panel.style) return; // shouldn't happen, but be sure
24 var panelBorder = panel.style.border;
 
 
25
26 // Disable animation if this browser doesn't support CSS transitions.
27 //
28 // We need this ugly calling form for old browsers that don't allow
29 // panel.style.hasOwnProperty('transition'); catering to old browsers
30 // is the whole point here.
31 var animate = panel.style.transition !== null && (typeof(panel.style.transition) == "string");
32 var animMS = 400;
 
 
 
 
 
 
 
 
 
 
 
 
33
34 // Calculate panel height despite its being hidden at call time.
35 // Based on https://stackoverflow.com/a/29047447/142454
36 var panelHeight; // computed on sitemap load
37 function calculatePanelHeight() {
 
 
 
 
 
38 // Get initial panel styles so we can restore them below.
39 var es = window.getComputedStyle(panel),
40 edis = es.display,
41 epos = es.position,
42 evis = es.visibility;
@@ -62,20 +88,55 @@
62 // instead, that would prevent the browser from seeing the height
63 // change as a state transition, so it'd skip the CSS transition:
64 //
65 // https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions#JavaScript_examples
66 function showPanel() {
 
 
 
 
 
 
 
67 if (animate) {
 
 
 
 
 
 
 
 
 
 
 
68 setTimeout(function() {
69 panel.style.maxHeight = panelHeight;
70 panel.style.border = panelBorder;
71 }, 40); // 25ms is insufficient with Firefox 62
72 }
73 else {
74 panel.style.display = 'block';
75 }
76 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
78 // Return true if the panel is showing.
79 function panelShowing() {
80 if (animate) {
81 return panel.style.maxHeight == panelHeight;
@@ -82,63 +143,92 @@
82 }
83 else {
84 return panel.style.display == 'block';
85 }
86 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
88 // Click handler for the hamburger button.
89 var needSitemapHTML = true;
90 document.querySelector("div.mainmenu > a").onclick = function() {
 
 
 
 
 
 
 
91 if (panelShowing()) {
 
 
92 // Transition back to hidden state.
93 if (animate) {
94 panel.style.maxHeight = '0';
95 setTimeout(function() {
96 // Browsers show a 1px high border line when maxHeight == 0,
97 // our "hidden" state, so hide the borders in that state, too.
98 panel.style.border = 'none';
99 }, animMS);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100 }
101 else {
102 panel.style.display = 'none';
103 }
104 }
105 else if (needSitemapHTML) {
106 // Only get it once per page load: it isn't likely to
107 // change on us.
108 var xhr = new XMLHttpRequest();
109 xhr.onload = function() {
110 var doc = xhr.responseXML;
111 if (doc) {
112 var sm = doc.querySelector("ul#sitemap");
113 if (sm && xhr.status == 200) {
114 // Got sitemap. Insert it into the drop-down panel.
115 needSitemapHTML = false;
116 panel.innerHTML = sm.outerHTML;
117
118 // Display the panel
119 if (animate) {
120 // Set up a CSS transition to animate the panel open and
121 // closed. Only needs to be done once per page load.
122 // Based on https://stackoverflow.com/a/29047447/142454
123 calculatePanelHeight();
124 panel.style.transition = 'max-height ' + animMS +
125 'ms ease-in-out';
126 panel.style.overflowY = 'hidden';
127 panel.style.maxHeight = '0';
128 showPanel();
129 }
130 panel.style.display = 'block';
131 }
132 }
133 // else, can't parse response as HTML or XML
134 }
135 xhr.open("GET", "$home/sitemap?popup"); // note the TH1 substitution!
136 xhr.responseType = "document";
137 xhr.send();
138 }
139 else {
140 showPanel(); // just show what we built above
141 }
142 return false; // prevent browser from acting on <a> click
143 }
144 })();
145
--- skins/default/js.txt
+++ skins/default/js.txt
@@ -16,27 +16,53 @@
16 ** This file contains the JS code specific to the Fossil default skin.
17 ** Currently, the only thing this does is handle clicks on its hamburger
18 ** menu button.
19 */
20 (function() {
21 var hbButton = document.getElementById("hbbtn");
22 if (!hbButton) return; // no hamburger button
23 if (!document.addEventListener) {
24 // Turn the button into a link to the sitemap for incompatible browsers.
25 hbButton.href = "$home/sitemap";
26 return;
27 }
28 var panel = document.getElementById("hbdrop");
29 if (!panel) return; // site admin might've nuked it
30 if (!panel.style) return; // shouldn't happen, but be sure
31 var panelBorder = panel.style.border;
32 var panelInitialized = false; // reset if browser window is resized
33 var panelResetBorderTimerID = 0; // used to cancel post-animation tasks
34
35 // Disable animation if this browser doesn't support CSS transitions.
36 //
37 // We need this ugly calling form for old browsers that don't allow
38 // panel.style.hasOwnProperty('transition'); catering to old browsers
39 // is the whole point here.
40 var animate = panel.style.transition !== null && (typeof(panel.style.transition) == "string");
41
42 // The duration of the animation can be overridden from the default skin
43 // header.txt by setting the "data-anim-ms" attribute of the panel.
44 var animMS = panel.getAttribute("data-anim-ms");
45 if (animMS) { // not null or empty string, parse it
46 animMS = parseInt(animMS);
47 if (isNaN(animMS) || animMS == 0)
48 animate = false; // disable animation if non-numeric or zero
49 else if (animMS < 0)
50 animMS = 400; // set default animation duration if negative
51 }
52 else // attribute is null or empty string, use default
53 animMS = 400;
54
55 // Calculate panel height despite its being hidden at call time.
56 // Based on https://stackoverflow.com/a/29047447/142454
57 var panelHeight; // computed on first panel display
58 function calculatePanelHeight() {
59
60 // Clear the max-height CSS property in case the panel size is recalculated
61 // after the browser window was resized.
62 panel.style.maxHeight = '';
63
64 // Get initial panel styles so we can restore them below.
65 var es = window.getComputedStyle(panel),
66 edis = es.display,
67 epos = es.position,
68 evis = es.visibility;
@@ -62,20 +88,55 @@
88 // instead, that would prevent the browser from seeing the height
89 // change as a state transition, so it'd skip the CSS transition:
90 //
91 // https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions#JavaScript_examples
92 function showPanel() {
93 // Cancel the timer to remove the panel border after the closing animation,
94 // otherwise double-clicking the hamburger button with the panel opened will
95 // remove the borders from the (closed and immediately reopened) panel.
96 if (panelResetBorderTimerID) {
97 clearTimeout(panelResetBorderTimerID);
98 panelResetBorderTimerID = 0;
99 }
100 if (animate) {
101 if (!panelInitialized) {
102 panelInitialized = true;
103 // Set up a CSS transition to animate the panel open and
104 // closed. Only needs to be done once per page load.
105 // Based on https://stackoverflow.com/a/29047447/142454
106 calculatePanelHeight();
107 panel.style.transition = 'max-height ' + animMS +
108 'ms ease-in-out';
109 panel.style.overflowY = 'hidden';
110 panel.style.maxHeight = '0';
111 }
112 setTimeout(function() {
113 panel.style.maxHeight = panelHeight;
114 panel.style.border = panelBorder;
115 }, 40); // 25ms is insufficient with Firefox 62
116 }
117 panel.style.display = 'block';
118 document.addEventListener('keydown',panelKeydown,/* useCapture == */true);
119 document.addEventListener('click',panelClick,false);
120 }
121
122 var panelKeydown = function(event) {
123 var key = event.which || event.keyCode;
124 if (key == 27) {
125 event.stopPropagation(); // ignore other keydown handlers
126 panelToggle(true);
127 }
128 };
129
130 var panelClick = function(event) {
131 if (!panel.contains(event.target)) {
132 // Call event.preventDefault() to have clicks outside the opened panel
133 // just close the panel, and swallow clicks on links or form elements.
134 //event.preventDefault();
135 panelToggle(true);
136 }
137 };
138
139 // Return true if the panel is showing.
140 function panelShowing() {
141 if (animate) {
142 return panel.style.maxHeight == panelHeight;
@@ -82,63 +143,92 @@
143 }
144 else {
145 return panel.style.display == 'block';
146 }
147 }
148
149 // Check if the specified HTML element has any child elements. Note that plain
150 // text nodes, comments, and any spaces (presentational or not) are ignored.
151 function hasChildren(element) {
152 var childElement = element.firstChild;
153 while (childElement) {
154 if (childElement.nodeType == 1) // Node.ELEMENT_NODE == 1
155 return true;
156 childElement = childElement.nextSibling;
157 }
158 return false;
159 }
160
161 // Reset the state of the panel to uninitialized if the browser window is
162 // resized, so the dimensions are recalculated the next time it's opened.
163 window.addEventListener('resize',function(event) {
164 panelInitialized = false;
165 },false);
166
167 // Click handler for the hamburger button.
168 hbButton.addEventListener('click',function(event) {
169 // Break the event handler chain, or the handler for document → click
170 // (about to be installed) may already be triggered by the current event.
171 event.stopPropagation();
172 event.preventDefault(); // prevent browser from acting on <a> click
173 panelToggle(false);
174 },false);
175
176 function panelToggle(suppressAnimation) {
177 if (panelShowing()) {
178 document.removeEventListener('keydown',panelKeydown,/* useCapture == */true);
179 document.removeEventListener('click',panelClick,false);
180 // Transition back to hidden state.
181 if (animate) {
182 if (suppressAnimation) {
183 var transition = panel.style.transition;
184 panel.style.transition = '';
185 panel.style.maxHeight = '0';
186 panel.style.border = 'none';
187 setTimeout(function() {
188 // Make sure CSS transition won't take effect now, so restore it
189 // asynchronously. Outer variable 'transition' still valid here.
190 panel.style.transition = transition;
191 }, 40); // 25ms is insufficient with Firefox 62
192 }
193 else {
194 panel.style.maxHeight = '0';
195 panelResetBorderTimerID = setTimeout(function() {
196 // Browsers show a 1px high border line when maxHeight == 0,
197 // our "hidden" state, so hide the borders in that state, too.
198 panel.style.border = 'none';
199 panelResetBorderTimerID = 0; // clear ID of completed timer
200 }, animMS);
201 }
202 }
203 else {
204 panel.style.display = 'none';
205 }
206 }
207 else {
208 if (!hasChildren(panel)) {
209 // Only get the sitemap once per page load: it isn't likely to
210 // change on us.
211 var xhr = new XMLHttpRequest();
212 xhr.onload = function() {
213 var doc = xhr.responseXML;
214 if (doc) {
215 var sm = doc.querySelector("ul#sitemap");
216 if (sm && xhr.status == 200) {
217 // Got sitemap. Insert it into the drop-down panel.
218 panel.innerHTML = sm.outerHTML;
219 // Display the panel
 
 
 
 
 
 
 
 
 
 
220 showPanel();
221 }
222 }
223 // else, can't parse response as HTML or XML
224 }
225 xhr.open("GET", "$home/sitemap?popup"); // note the TH1 substitution!
226 xhr.responseType = "document";
227 xhr.send();
228 }
229 else {
230 showPanel(); // just show what we built above
231 }
232 }
 
 
233 }
234 })();
235
--- src/default_css.txt
+++ src/default_css.txt
@@ -219,10 +219,11 @@
219219
div.columns > ul li:first-child {
220220
margin-top:0px;
221221
}
222222
.columns li {
223223
break-inside: avoid;
224
+ page-break-inside: avoid;
224225
}
225226
.filetree {
226227
margin: 1em 0;
227228
line-height: 1.5;
228229
}
229230
--- src/default_css.txt
+++ src/default_css.txt
@@ -219,10 +219,11 @@
219 div.columns > ul li:first-child {
220 margin-top:0px;
221 }
222 .columns li {
223 break-inside: avoid;
 
224 }
225 .filetree {
226 margin: 1em 0;
227 line-height: 1.5;
228 }
229
--- src/default_css.txt
+++ src/default_css.txt
@@ -219,10 +219,11 @@
219 div.columns > ul li:first-child {
220 margin-top:0px;
221 }
222 .columns li {
223 break-inside: avoid;
224 page-break-inside: avoid;
225 }
226 .filetree {
227 margin: 1em 0;
228 line-height: 1.5;
229 }
230
--- src/default_css.txt
+++ src/default_css.txt
@@ -219,10 +219,11 @@
219219
div.columns > ul li:first-child {
220220
margin-top:0px;
221221
}
222222
.columns li {
223223
break-inside: avoid;
224
+ page-break-inside: avoid;
224225
}
225226
.filetree {
226227
margin: 1em 0;
227228
line-height: 1.5;
228229
}
229230
--- src/default_css.txt
+++ src/default_css.txt
@@ -219,10 +219,11 @@
219 div.columns > ul li:first-child {
220 margin-top:0px;
221 }
222 .columns li {
223 break-inside: avoid;
 
224 }
225 .filetree {
226 margin: 1em 0;
227 line-height: 1.5;
228 }
229
--- src/default_css.txt
+++ src/default_css.txt
@@ -219,10 +219,11 @@
219 div.columns > ul li:first-child {
220 margin-top:0px;
221 }
222 .columns li {
223 break-inside: avoid;
224 page-break-inside: avoid;
225 }
226 .filetree {
227 margin: 1em 0;
228 line-height: 1.5;
229 }
230
--- www/customskin.md
+++ www/customskin.md
@@ -1,16 +1,16 @@
11
Theming
22
=======
33
44
Every HTML page generated by Fossil has the following basic structure:
55
6
-
76
<blockquote><table border=1 cellpadding=10><tbody>
87
<tr><td style='background-color:lightblue;text-align:center;'>Header</td></tr>
98
<tr><td style='background-color:lightgreen;text-align:center;'>
109
Fossil-Generated Content</td></tr>
1110
<tr><td style='background-color:lightblue;text-align:center;'>Footer</td></tr>
11
+<tr><td style='background-color:lightyellow;text-align:center;'>Javascript (optional)</td></tr>
1212
</tbody></table></blockquote>
1313
1414
The header and footer control the "look" of Fossil pages. Those
1515
two sections can be customized separately for each repository to
1616
develop a new theme.
@@ -134,10 +134,69 @@
134134
135135
The same TH1 interpreter is used for both the header and the footer
136136
and for all scripts contained within them both. Hence, any global
137137
TH1 variables that are set by the header are available to the footer.
138138
139
+Customizing the ≡ Hamburger Menu
140
+--------------------------------
141
+
142
+The menu bar of the default skin has an entry to open a drop-down menu with
143
+additional navigation links, represented by the ≡ button (hence the name
144
+"hamburger menu"). The Javascript logic to open and close the hamburger menu
145
+when the button is clicked is contained in the optional Javascript part (js.txt)
146
+of the default skin. Out of the box, the drop-down menu shows the [Site
147
+Map](../../../sitemap), loaded by an AJAX request prior to the first display.
148
+
149
+The ≡ button for the hamburger menu is added to the menu bar by the following
150
+TH1 command in the default skin header.txt, right before the menu bar links:
151
+
152
+ html "<a id='hbbtn' href='#'>&#9776;</a>"
153
+
154
+The hamburger button can be repositioned between the other menu links (but the
155
+drop-down menu is always left-aligned with the menu bar), or it can be removed
156
+by deleting the above statement (the Javascript logic detects this case and
157
+remains idle, so it's not necessary to modify the default skin js.txt).
158
+
159
+The following empty element at the bottom of the default skin header.txt serves
160
+as the panel to hold the drop-down menu elements:
161
+
162
+ <div id='hbdrop'></div>
163
+
164
+Out of the box, the contents of the panel is populated with the [Site
165
+Map](../../../sitemap), but only if the panel does not already contain any HTML
166
+elements (that is, not just comments, plain text or non-presentational white
167
+space). So the hamburger menu can be customized by replacing the empty `<div
168
+id='hbdrop'></div>` element with a menu structure knitted according to the
169
+following template:
170
+
171
+ <div id="hbdrop" data-anim-ms="400">
172
+ <ul class="columns" style="column-width: 20em; column-count: auto">
173
+ <!-- NEW GROUP WITH HEADING LINK -->
174
+ <li>
175
+ <a href="$home$index_page">Link: Home</a>
176
+ <ul>
177
+ <li><a href="$home/timeline">Link: Timeline</a></li>
178
+ <li><a href="$home/dir?ci=tip">Link: File List</a></li>
179
+ </ul>
180
+ </li>
181
+ <!-- NEW GROUP WITH HEADING TEXT -->
182
+ <li>
183
+ Heading Text
184
+ <ul>
185
+ <li><a href="$home/doc/trunk/www/customskin.md">Link: Theming</a></li>
186
+ <li><a href="$home/doc/trunk/www/th1.md">Link: TH1 Scripts</a></li>
187
+ </ul>
188
+ </li>
189
+ <!-- NEXT GROUP GOES HERE -->
190
+ </ul>
191
+ </div>
192
+
193
+The custom `data-anim-ms` attribute can be added to the panel element to direct
194
+the Javascript logic to override the default menu animation duration of 400 ms.
195
+A faster animation duration of 80-200 ms may be preferred for smaller menus. The
196
+animation is disabled by setting the attribute to `"0"`.
197
+
139198
TH1 Variables
140199
-------------
141200
142201
Before expanding the TH1 within the header and footer, Fossil first
143202
initializes a number of TH1 variables to values that depend on
144203
--- www/customskin.md
+++ www/customskin.md
@@ -1,16 +1,16 @@
1 Theming
2 =======
3
4 Every HTML page generated by Fossil has the following basic structure:
5
6
7 <blockquote><table border=1 cellpadding=10><tbody>
8 <tr><td style='background-color:lightblue;text-align:center;'>Header</td></tr>
9 <tr><td style='background-color:lightgreen;text-align:center;'>
10 Fossil-Generated Content</td></tr>
11 <tr><td style='background-color:lightblue;text-align:center;'>Footer</td></tr>
 
12 </tbody></table></blockquote>
13
14 The header and footer control the "look" of Fossil pages. Those
15 two sections can be customized separately for each repository to
16 develop a new theme.
@@ -134,10 +134,69 @@
134
135 The same TH1 interpreter is used for both the header and the footer
136 and for all scripts contained within them both. Hence, any global
137 TH1 variables that are set by the header are available to the footer.
138
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139 TH1 Variables
140 -------------
141
142 Before expanding the TH1 within the header and footer, Fossil first
143 initializes a number of TH1 variables to values that depend on
144
--- www/customskin.md
+++ www/customskin.md
@@ -1,16 +1,16 @@
1 Theming
2 =======
3
4 Every HTML page generated by Fossil has the following basic structure:
5
 
6 <blockquote><table border=1 cellpadding=10><tbody>
7 <tr><td style='background-color:lightblue;text-align:center;'>Header</td></tr>
8 <tr><td style='background-color:lightgreen;text-align:center;'>
9 Fossil-Generated Content</td></tr>
10 <tr><td style='background-color:lightblue;text-align:center;'>Footer</td></tr>
11 <tr><td style='background-color:lightyellow;text-align:center;'>Javascript (optional)</td></tr>
12 </tbody></table></blockquote>
13
14 The header and footer control the "look" of Fossil pages. Those
15 two sections can be customized separately for each repository to
16 develop a new theme.
@@ -134,10 +134,69 @@
134
135 The same TH1 interpreter is used for both the header and the footer
136 and for all scripts contained within them both. Hence, any global
137 TH1 variables that are set by the header are available to the footer.
138
139 Customizing the ≡ Hamburger Menu
140 --------------------------------
141
142 The menu bar of the default skin has an entry to open a drop-down menu with
143 additional navigation links, represented by the ≡ button (hence the name
144 "hamburger menu"). The Javascript logic to open and close the hamburger menu
145 when the button is clicked is contained in the optional Javascript part (js.txt)
146 of the default skin. Out of the box, the drop-down menu shows the [Site
147 Map](../../../sitemap), loaded by an AJAX request prior to the first display.
148
149 The ≡ button for the hamburger menu is added to the menu bar by the following
150 TH1 command in the default skin header.txt, right before the menu bar links:
151
152 html "<a id='hbbtn' href='#'>&#9776;</a>"
153
154 The hamburger button can be repositioned between the other menu links (but the
155 drop-down menu is always left-aligned with the menu bar), or it can be removed
156 by deleting the above statement (the Javascript logic detects this case and
157 remains idle, so it's not necessary to modify the default skin js.txt).
158
159 The following empty element at the bottom of the default skin header.txt serves
160 as the panel to hold the drop-down menu elements:
161
162 <div id='hbdrop'></div>
163
164 Out of the box, the contents of the panel is populated with the [Site
165 Map](../../../sitemap), but only if the panel does not already contain any HTML
166 elements (that is, not just comments, plain text or non-presentational white
167 space). So the hamburger menu can be customized by replacing the empty `<div
168 id='hbdrop'></div>` element with a menu structure knitted according to the
169 following template:
170
171 <div id="hbdrop" data-anim-ms="400">
172 <ul class="columns" style="column-width: 20em; column-count: auto">
173 <!-- NEW GROUP WITH HEADING LINK -->
174 <li>
175 <a href="$home$index_page">Link: Home</a>
176 <ul>
177 <li><a href="$home/timeline">Link: Timeline</a></li>
178 <li><a href="$home/dir?ci=tip">Link: File List</a></li>
179 </ul>
180 </li>
181 <!-- NEW GROUP WITH HEADING TEXT -->
182 <li>
183 Heading Text
184 <ul>
185 <li><a href="$home/doc/trunk/www/customskin.md">Link: Theming</a></li>
186 <li><a href="$home/doc/trunk/www/th1.md">Link: TH1 Scripts</a></li>
187 </ul>
188 </li>
189 <!-- NEXT GROUP GOES HERE -->
190 </ul>
191 </div>
192
193 The custom `data-anim-ms` attribute can be added to the panel element to direct
194 the Javascript logic to override the default menu animation duration of 400 ms.
195 A faster animation duration of 80-200 ms may be preferred for smaller menus. The
196 animation is disabled by setting the attribute to `"0"`.
197
198 TH1 Variables
199 -------------
200
201 Before expanding the TH1 within the header and footer, Fossil first
202 initializes a number of TH1 variables to values that depend on
203

Keyboard Shortcuts

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