Fossil SCM

Switch away from classic (bubbling-only) event handling to simplify management of temporary handlers, and to have the ESC key listener use a priority (capturing) handler, to prevent any other keydown handlers or default actions for the ESC key while the panel is open. This does not change the compatibility test results summarized here: [https://fossil-scm.org/forum/forumpost/f425a1756c]. (IE8 halts JS processing at JSON.parse in antiRobotDefense, anyway.) Also, for non-compatible browsers, there's a new fallback to transform the hamburger button into a simple (non-scripted) link to the sitemap.

florian 2018-10-12 16:16 UTC js-hamburger-menu
Commit e3376829e211a3f2d4fc566619423a307b6d23b0
1 file changed +39 -36
--- skins/default/js.txt
+++ skins/default/js.txt
@@ -16,11 +16,17 @@
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
- if (!document.getElementById("hbbtn")) return; // no hamburger button
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
+ }
2228
var panel = document.getElementById("hbdrop");
2329
if (!panel) return; // site admin might've nuked it
2430
if (!panel.style) return; // shouldn't happen, but be sure
2531
var panelBorder = panel.style.border;
2632
var panelInitialized = false; // reset if browser window is resized
@@ -39,12 +45,10 @@
3945
if (animMS === "0")
4046
animate = false; // disable animation if "data-anim-ms" === "0"
4147
else if (!animMS || animMS>>0 !== Number(animMS) || animMS < 0)
4248
animMS = 400; // set default if missing, empty, non-integer, or negative
4349
44
- var originalEventHandlers = { }; // original event handlers to be restored
45
-
4650
// Calculate panel height despite its being hidden at call time.
4751
// Based on https://stackoverflow.com/a/29047447/142454
4852
var panelHeight; // computed on first panel display
4953
function calculatePanelHeight() {
5054
@@ -104,27 +108,32 @@
104108
panel.style.maxHeight = panelHeight;
105109
panel.style.border = panelBorder;
106110
}, 40); // 25ms is insufficient with Firefox 62
107111
}
108112
panel.style.display = 'block';
109
- originalEventHandlers.onkeydown = document.onkeydown;
110
- document.onkeydown = function(event) {
111
- event = event || window.event;
112
- var key = event.which || event.keyCode;
113
- if (key == 27) {
114
- panelToggle(true);
115
- }
116
- };
117
- originalEventHandlers.onclick = document.onclick;
118
- document.onclick = function(event) {
119
- event = event || window.event;
120
- if (!panel.contains(event.target)) {
121
- panelToggle(true);
122
- //return false; // prevent default action (i.e. open clicked links)
123
- }
124
- };
125
- }
113
+ document.addEventListener('keydown',panelKeydown,/* useCapture == */true);
114
+ document.addEventListener('click',panelClick,false);
115
+ }
116
+
117
+ var panelKeydown = function(event) {
118
+ event = event || window.event;
119
+ var key = event.which || event.keyCode;
120
+ if (key == 27) {
121
+ event.stopPropagation(); // ignore other keydown handlers
122
+ panelToggle(true);
123
+ }
124
+ };
125
+
126
+ var panelClick = function(event) {
127
+ event = event || window.event;
128
+ if (!panel.contains(event.target)) {
129
+ // Call event.preventDefault() to have clicks outside the opened panel
130
+ // just close the panel, and swallow clicks on links or form elements.
131
+ //event.preventDefault();
132
+ panelToggle(true);
133
+ }
134
+ };
126135
127136
// Return true if the panel is showing.
128137
function panelShowing() {
129138
if (animate) {
130139
return panel.style.maxHeight == panelHeight;
@@ -146,33 +155,27 @@
146155
return false;
147156
}
148157
149158
// Reset the state of the panel to uninitialized if the browser window is
150159
// resized, so the dimensions are recalculated the next time it's opened.
151
- originalEventHandlers.onresize = window.onresize;
152
- window.onresize = function(event) {
160
+ window.addEventListener('resize',function(event) {
153161
panelInitialized = false;
154
- if (originalEventHandlers.onresize) {
155
- originalEventHandlers.onresize.call(window,event);
156
- }
157
- };
162
+ },false);
158163
159164
// Click handler for the hamburger button.
160
- document.getElementById("hbbtn").onclick = function(event) {
161
- // Break the event handler chain, or the handler for document.onclick
165
+ hbButton.addEventListener('click',function(event) {
166
+ // Break the event handler chain, or the handler for document → click
162167
// (about to be installed) may already be triggered by the current event.
163
- if (event.stopPropagation)
164
- event.stopPropagation();
165
- else
166
- event.cancelBubble = true;
168
+ event.stopPropagation();
169
+ event.preventDefault(); // prevent browser from acting on <a> click
167170
panelToggle(false);
168
- return false; // prevent browser from acting on <a> click
169
- };
171
+ },false);
172
+
170173
function panelToggle(suppressAnimation) {
171174
if (panelShowing()) {
172
- document.onkeydown = originalEventHandlers.onkeydown;
173
- document.onclick = originalEventHandlers.onclick;
175
+ document.removeEventListener('keydown',panelKeydown,/* useCapture == */true);
176
+ document.removeEventListener('click',panelClick,false);
174177
// Transition back to hidden state.
175178
if (animate) {
176179
if (suppressAnimation) {
177180
var transition = panel.style.transition;
178181
panel.style.transition = '';
179182
--- skins/default/js.txt
+++ skins/default/js.txt
@@ -16,11 +16,17 @@
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 if (!document.getElementById("hbbtn")) return; // no hamburger button
 
 
 
 
 
 
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
@@ -39,12 +45,10 @@
39 if (animMS === "0")
40 animate = false; // disable animation if "data-anim-ms" === "0"
41 else if (!animMS || animMS>>0 !== Number(animMS) || animMS < 0)
42 animMS = 400; // set default if missing, empty, non-integer, or negative
43
44 var originalEventHandlers = { }; // original event handlers to be restored
45
46 // Calculate panel height despite its being hidden at call time.
47 // Based on https://stackoverflow.com/a/29047447/142454
48 var panelHeight; // computed on first panel display
49 function calculatePanelHeight() {
50
@@ -104,27 +108,32 @@
104 panel.style.maxHeight = panelHeight;
105 panel.style.border = panelBorder;
106 }, 40); // 25ms is insufficient with Firefox 62
107 }
108 panel.style.display = 'block';
109 originalEventHandlers.onkeydown = document.onkeydown;
110 document.onkeydown = function(event) {
111 event = event || window.event;
112 var key = event.which || event.keyCode;
113 if (key == 27) {
114 panelToggle(true);
115 }
116 };
117 originalEventHandlers.onclick = document.onclick;
118 document.onclick = function(event) {
119 event = event || window.event;
120 if (!panel.contains(event.target)) {
121 panelToggle(true);
122 //return false; // prevent default action (i.e. open clicked links)
123 }
124 };
125 }
 
 
 
 
 
126
127 // Return true if the panel is showing.
128 function panelShowing() {
129 if (animate) {
130 return panel.style.maxHeight == panelHeight;
@@ -146,33 +155,27 @@
146 return false;
147 }
148
149 // Reset the state of the panel to uninitialized if the browser window is
150 // resized, so the dimensions are recalculated the next time it's opened.
151 originalEventHandlers.onresize = window.onresize;
152 window.onresize = function(event) {
153 panelInitialized = false;
154 if (originalEventHandlers.onresize) {
155 originalEventHandlers.onresize.call(window,event);
156 }
157 };
158
159 // Click handler for the hamburger button.
160 document.getElementById("hbbtn").onclick = function(event) {
161 // Break the event handler chain, or the handler for document.onclick
162 // (about to be installed) may already be triggered by the current event.
163 if (event.stopPropagation)
164 event.stopPropagation();
165 else
166 event.cancelBubble = true;
167 panelToggle(false);
168 return false; // prevent browser from acting on <a> click
169 };
170 function panelToggle(suppressAnimation) {
171 if (panelShowing()) {
172 document.onkeydown = originalEventHandlers.onkeydown;
173 document.onclick = originalEventHandlers.onclick;
174 // Transition back to hidden state.
175 if (animate) {
176 if (suppressAnimation) {
177 var transition = panel.style.transition;
178 panel.style.transition = '';
179
--- skins/default/js.txt
+++ skins/default/js.txt
@@ -16,11 +16,17 @@
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
@@ -39,12 +45,10 @@
45 if (animMS === "0")
46 animate = false; // disable animation if "data-anim-ms" === "0"
47 else if (!animMS || animMS>>0 !== Number(animMS) || animMS < 0)
48 animMS = 400; // set default if missing, empty, non-integer, or negative
49
 
 
50 // Calculate panel height despite its being hidden at call time.
51 // Based on https://stackoverflow.com/a/29047447/142454
52 var panelHeight; // computed on first panel display
53 function calculatePanelHeight() {
54
@@ -104,27 +108,32 @@
108 panel.style.maxHeight = panelHeight;
109 panel.style.border = panelBorder;
110 }, 40); // 25ms is insufficient with Firefox 62
111 }
112 panel.style.display = 'block';
113 document.addEventListener('keydown',panelKeydown,/* useCapture == */true);
114 document.addEventListener('click',panelClick,false);
115 }
116
117 var panelKeydown = function(event) {
118 event = event || window.event;
119 var key = event.which || event.keyCode;
120 if (key == 27) {
121 event.stopPropagation(); // ignore other keydown handlers
122 panelToggle(true);
123 }
124 };
125
126 var panelClick = function(event) {
127 event = event || window.event;
128 if (!panel.contains(event.target)) {
129 // Call event.preventDefault() to have clicks outside the opened panel
130 // just close the panel, and swallow clicks on links or form elements.
131 //event.preventDefault();
132 panelToggle(true);
133 }
134 };
135
136 // Return true if the panel is showing.
137 function panelShowing() {
138 if (animate) {
139 return panel.style.maxHeight == panelHeight;
@@ -146,33 +155,27 @@
155 return false;
156 }
157
158 // Reset the state of the panel to uninitialized if the browser window is
159 // resized, so the dimensions are recalculated the next time it's opened.
160 window.addEventListener('resize',function(event) {
 
161 panelInitialized = false;
162 },false);
 
 
 
163
164 // Click handler for the hamburger button.
165 hbButton.addEventListener('click',function(event) {
166 // Break the event handler chain, or the handler for document → click
167 // (about to be installed) may already be triggered by the current event.
168 event.stopPropagation();
169 event.preventDefault(); // prevent browser from acting on <a> click
 
 
170 panelToggle(false);
171 },false);
172
173 function panelToggle(suppressAnimation) {
174 if (panelShowing()) {
175 document.removeEventListener('keydown',panelKeydown,/* useCapture == */true);
176 document.removeEventListener('click',panelClick,false);
177 // Transition back to hidden state.
178 if (animate) {
179 if (suppressAnimation) {
180 var transition = panel.style.transition;
181 panel.style.transition = '';
182

Keyboard Shortcuts

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