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.
Commit
e3376829e211a3f2d4fc566619423a307b6d23b0
Parent
c73deeb69abaa05…
1 file changed
+39
-36
+39
-36
| --- skins/default/js.txt | ||
| +++ skins/default/js.txt | ||
| @@ -16,11 +16,17 @@ | ||
| 16 | 16 | ** This file contains the JS code specific to the Fossil default skin. |
| 17 | 17 | ** Currently, the only thing this does is handle clicks on its hamburger |
| 18 | 18 | ** menu button. |
| 19 | 19 | */ |
| 20 | 20 | (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 | + } | |
| 22 | 28 | var panel = document.getElementById("hbdrop"); |
| 23 | 29 | if (!panel) return; // site admin might've nuked it |
| 24 | 30 | if (!panel.style) return; // shouldn't happen, but be sure |
| 25 | 31 | var panelBorder = panel.style.border; |
| 26 | 32 | var panelInitialized = false; // reset if browser window is resized |
| @@ -39,12 +45,10 @@ | ||
| 39 | 45 | if (animMS === "0") |
| 40 | 46 | animate = false; // disable animation if "data-anim-ms" === "0" |
| 41 | 47 | else if (!animMS || animMS>>0 !== Number(animMS) || animMS < 0) |
| 42 | 48 | animMS = 400; // set default if missing, empty, non-integer, or negative |
| 43 | 49 | |
| 44 | - var originalEventHandlers = { }; // original event handlers to be restored | |
| 45 | - | |
| 46 | 50 | // Calculate panel height despite its being hidden at call time. |
| 47 | 51 | // Based on https://stackoverflow.com/a/29047447/142454 |
| 48 | 52 | var panelHeight; // computed on first panel display |
| 49 | 53 | function calculatePanelHeight() { |
| 50 | 54 | |
| @@ -104,27 +108,32 @@ | ||
| 104 | 108 | panel.style.maxHeight = panelHeight; |
| 105 | 109 | panel.style.border = panelBorder; |
| 106 | 110 | }, 40); // 25ms is insufficient with Firefox 62 |
| 107 | 111 | } |
| 108 | 112 | 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 | + }; | |
| 126 | 135 | |
| 127 | 136 | // Return true if the panel is showing. |
| 128 | 137 | function panelShowing() { |
| 129 | 138 | if (animate) { |
| 130 | 139 | return panel.style.maxHeight == panelHeight; |
| @@ -146,33 +155,27 @@ | ||
| 146 | 155 | return false; |
| 147 | 156 | } |
| 148 | 157 | |
| 149 | 158 | // Reset the state of the panel to uninitialized if the browser window is |
| 150 | 159 | // 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) { | |
| 153 | 161 | panelInitialized = false; |
| 154 | - if (originalEventHandlers.onresize) { | |
| 155 | - originalEventHandlers.onresize.call(window,event); | |
| 156 | - } | |
| 157 | - }; | |
| 162 | + },false); | |
| 158 | 163 | |
| 159 | 164 | // 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 | |
| 162 | 167 | // (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 | |
| 167 | 170 | panelToggle(false); |
| 168 | - return false; // prevent browser from acting on <a> click | |
| 169 | - }; | |
| 171 | + },false); | |
| 172 | + | |
| 170 | 173 | function panelToggle(suppressAnimation) { |
| 171 | 174 | 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); | |
| 174 | 177 | // Transition back to hidden state. |
| 175 | 178 | if (animate) { |
| 176 | 179 | if (suppressAnimation) { |
| 177 | 180 | var transition = panel.style.transition; |
| 178 | 181 | panel.style.transition = ''; |
| 179 | 182 |
| --- 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 |