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.
Commit
9cd74289c0a76606becb4bf66cac55f637c00dd9421acfd428992ec0fc2ca661
Parent
18e2f5337f112f6…
10 files changed
-3
+1
-228
+1
-3
+31
-9
+250
+1
-1
+27
+1
-1
+2
-2
| --- skins/default/footer.txt | ||
| +++ skins/default/footer.txt | ||
| @@ -1,8 +1,5 @@ | ||
| 1 | 1 | <div class="footer"> |
| 2 | 2 | This page was generated in about |
| 3 | 3 | <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by |
| 4 | 4 | Fossil $release_version $manifest_version $manifest_date |
| 5 | 5 | </div> |
| 6 | -<script nonce="$nonce"> | |
| 7 | -<th1>styleScript</th1> | |
| 8 | -</script> | |
| 9 | 6 |
| --- 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 @@ | ||
| 18 | 18 | } else { |
| 19 | 19 | html "<a href='$home$url' class='$cls'>$name</a>\n" |
| 20 | 20 | } |
| 21 | 21 | } |
| 22 | 22 | html "<a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>☰</a>" |
| 23 | +builtin_request_js hbmenu.js | |
| 23 | 24 | menulink $index_page Home {} |
| 24 | 25 | if {[anycap jor]} { |
| 25 | 26 | menulink /timeline Timeline {} |
| 26 | 27 | } |
| 27 | 28 | if {[hascap oh]} { |
| 28 | 29 | |
| 29 | 30 | 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'>☰</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'>☰</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 @@ | |
+1
-3
| --- skins/plain_gray/header.txt | ||
| +++ skins/plain_gray/header.txt | ||
| @@ -2,10 +2,11 @@ | ||
| 2 | 2 | <div class="title">$<project_name>: $<title></div> |
| 3 | 3 | </div> |
| 4 | 4 | <div class="mainmenu"> |
| 5 | 5 | <th1> |
| 6 | 6 | html "<a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>☰</a>" |
| 7 | +builtin_request_js hbmenu.js | |
| 7 | 8 | html "<a href='$home$index_page'>Home</a>\n" |
| 8 | 9 | if {[anycap jor]} { |
| 9 | 10 | html "<a href='$home/timeline'>Timeline</a>\n" |
| 10 | 11 | } |
| 11 | 12 | if {[anoncap oh]} { |
| @@ -32,8 +33,5 @@ | ||
| 32 | 33 | } else { |
| 33 | 34 | html "<a href='$home/login'>Login</a>\n" |
| 34 | 35 | } |
| 35 | 36 | </th1></div> |
| 36 | 37 | <div id='hbdrop' class='hbdrop'></div> |
| 37 | -<script nonce="$nonce"> | |
| 38 | -<th1>styleScript skins/default/js.txt</th1> | |
| 39 | -</script> | |
| 40 | 38 | |
| 41 | 39 | 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'>☰</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'>☰</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 @@ | ||
| 1 | 1 | /* |
| 2 | -** Copyright © 2018 Warren Young | |
| 2 | +** Originally: Copyright © 2018 Warren Young | |
| 3 | 3 | ** |
| 4 | 4 | ** This program is free software; you can redistribute it and/or |
| 5 | 5 | ** modify it under the terms of the Simplified BSD License (also |
| @@ -9,11 +9,37 @@ | ||
| 9 | 9 | ** but without any warranty; without even the implied warranty of |
| 10 | 10 | ** merchantability or fitness for a particular purpose. |
| 11 | 11 | ** |
| 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 | +** | |
| 13 | 15 | ******************************************************************************* |
| 14 | 16 | ** |
| 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. | |
| 17 | 43 | */ |
| 18 | 44 | (function() { |
| 19 | 45 | var hbButton = document.getElementById("hbbtn"); |
| @@ -221,8 +247,4 @@ | ||
| 221 | 247 | xhr.send(); |
| 222 | 248 | } |
| 223 | 249 | 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 @@ | ||
| 190 | 190 | $(SRCDIR)/../skins/bootstrap/header.txt \ |
| 191 | 191 | $(SRCDIR)/../skins/default/css.txt \ |
| 192 | 192 | $(SRCDIR)/../skins/default/details.txt \ |
| 193 | 193 | $(SRCDIR)/../skins/default/footer.txt \ |
| 194 | 194 | $(SRCDIR)/../skins/default/header.txt \ |
| 195 | - $(SRCDIR)/../skins/default/js.txt \ | |
| 196 | 195 | $(SRCDIR)/../skins/eagle/css.txt \ |
| 197 | 196 | $(SRCDIR)/../skins/eagle/details.txt \ |
| 198 | 197 | $(SRCDIR)/../skins/eagle/footer.txt \ |
| 199 | 198 | $(SRCDIR)/../skins/eagle/header.txt \ |
| 200 | 199 | $(SRCDIR)/../skins/enhanced1/css.txt \ |
| @@ -246,10 +245,11 @@ | ||
| 246 | 245 | $(SRCDIR)/fossil.popupwidget.js \ |
| 247 | 246 | $(SRCDIR)/fossil.storage.js \ |
| 248 | 247 | $(SRCDIR)/fossil.tabs.js \ |
| 249 | 248 | $(SRCDIR)/fossil.wikiedit-wysiwyg.js \ |
| 250 | 249 | $(SRCDIR)/graph.js \ |
| 250 | + $(SRCDIR)/hbmenu.js \ | |
| 251 | 251 | $(SRCDIR)/href.js \ |
| 252 | 252 | $(SRCDIR)/login.js \ |
| 253 | 253 | $(SRCDIR)/markdown.md \ |
| 254 | 254 | $(SRCDIR)/menu.js \ |
| 255 | 255 | $(SRCDIR)/sbsdiff.js \ |
| 256 | 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/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 |
+27
| --- src/th_main.c | ||
| +++ src/th_main.c | ||
| @@ -1492,10 +1492,15 @@ | ||
| 1492 | 1492 | /* |
| 1493 | 1493 | ** TH1 command: styleScript ?BUILTIN-FILENAME? |
| 1494 | 1494 | ** |
| 1495 | 1495 | ** Render the js.txt file from the current skin. Or, if an argument |
| 1496 | 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. | |
| 1497 | 1502 | */ |
| 1498 | 1503 | static int styleScriptCmd( |
| 1499 | 1504 | Th_Interp *interp, |
| 1500 | 1505 | void *p, |
| 1501 | 1506 | int argc, |
| @@ -1520,10 +1525,31 @@ | ||
| 1520 | 1525 | Th_SetResult(interp, "repository unavailable", -1); |
| 1521 | 1526 | return TH_ERROR; |
| 1522 | 1527 | } |
| 1523 | 1528 | } |
| 1524 | 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 | +} | |
| 1525 | 1551 | |
| 1526 | 1552 | /* |
| 1527 | 1553 | ** TH1 command: artifact ID ?FILENAME? |
| 1528 | 1554 | ** |
| 1529 | 1555 | ** Attempts to locate the specified artifact and return its contents. An |
| @@ -2237,10 +2263,11 @@ | ||
| 2237 | 2263 | void *pContext; |
| 2238 | 2264 | } aCommand[] = { |
| 2239 | 2265 | {"anoncap", hascapCmd, (void*)&anonFlag}, |
| 2240 | 2266 | {"anycap", anycapCmd, 0}, |
| 2241 | 2267 | {"artifact", artifactCmd, 0}, |
| 2268 | + {"builtin_request_js", builtinRequestJsCmd, 0}, | |
| 2242 | 2269 | {"captureTh1", captureTh1Cmd, 0}, |
| 2243 | 2270 | {"cgiHeaderLine", cgiHeaderLineCmd, 0}, |
| 2244 | 2271 | {"checkout", checkoutCmd, 0}, |
| 2245 | 2272 | {"combobox", comboboxCmd, 0}, |
| 2246 | 2273 | {"copybtn", copybtnCmd, 0}, |
| 2247 | 2274 |
| --- 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 |
+1
-1
| --- win/Makefile.mingw | ||
| +++ win/Makefile.mingw | ||
| @@ -602,11 +602,10 @@ | ||
| 602 | 602 | $(SRCDIR)/../skins/bootstrap/header.txt \ |
| 603 | 603 | $(SRCDIR)/../skins/default/css.txt \ |
| 604 | 604 | $(SRCDIR)/../skins/default/details.txt \ |
| 605 | 605 | $(SRCDIR)/../skins/default/footer.txt \ |
| 606 | 606 | $(SRCDIR)/../skins/default/header.txt \ |
| 607 | - $(SRCDIR)/../skins/default/js.txt \ | |
| 608 | 607 | $(SRCDIR)/../skins/eagle/css.txt \ |
| 609 | 608 | $(SRCDIR)/../skins/eagle/details.txt \ |
| 610 | 609 | $(SRCDIR)/../skins/eagle/footer.txt \ |
| 611 | 610 | $(SRCDIR)/../skins/eagle/header.txt \ |
| 612 | 611 | $(SRCDIR)/../skins/enhanced1/css.txt \ |
| @@ -658,10 +657,11 @@ | ||
| 658 | 657 | $(SRCDIR)/fossil.popupwidget.js \ |
| 659 | 658 | $(SRCDIR)/fossil.storage.js \ |
| 660 | 659 | $(SRCDIR)/fossil.tabs.js \ |
| 661 | 660 | $(SRCDIR)/fossil.wikiedit-wysiwyg.js \ |
| 662 | 661 | $(SRCDIR)/graph.js \ |
| 662 | + $(SRCDIR)/hbmenu.js \ | |
| 663 | 663 | $(SRCDIR)/href.js \ |
| 664 | 664 | $(SRCDIR)/login.js \ |
| 665 | 665 | $(SRCDIR)/markdown.md \ |
| 666 | 666 | $(SRCDIR)/menu.js \ |
| 667 | 667 | $(SRCDIR)/sbsdiff.js \ |
| 668 | 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/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 |
+2
-2
| --- win/Makefile.msc | ||
| +++ win/Makefile.msc | ||
| @@ -523,11 +523,10 @@ | ||
| 523 | 523 | "$(SRCDIR)\..\skins\bootstrap\header.txt" \ |
| 524 | 524 | "$(SRCDIR)\..\skins\default\css.txt" \ |
| 525 | 525 | "$(SRCDIR)\..\skins\default\details.txt" \ |
| 526 | 526 | "$(SRCDIR)\..\skins\default\footer.txt" \ |
| 527 | 527 | "$(SRCDIR)\..\skins\default\header.txt" \ |
| 528 | - "$(SRCDIR)\..\skins\default\js.txt" \ | |
| 529 | 528 | "$(SRCDIR)\..\skins\eagle\css.txt" \ |
| 530 | 529 | "$(SRCDIR)\..\skins\eagle\details.txt" \ |
| 531 | 530 | "$(SRCDIR)\..\skins\eagle\footer.txt" \ |
| 532 | 531 | "$(SRCDIR)\..\skins\eagle\header.txt" \ |
| 533 | 532 | "$(SRCDIR)\..\skins\enhanced1\css.txt" \ |
| @@ -579,10 +578,11 @@ | ||
| 579 | 578 | "$(SRCDIR)\fossil.popupwidget.js" \ |
| 580 | 579 | "$(SRCDIR)\fossil.storage.js" \ |
| 581 | 580 | "$(SRCDIR)\fossil.tabs.js" \ |
| 582 | 581 | "$(SRCDIR)\fossil.wikiedit-wysiwyg.js" \ |
| 583 | 582 | "$(SRCDIR)\graph.js" \ |
| 583 | + "$(SRCDIR)\hbmenu.js" \ | |
| 584 | 584 | "$(SRCDIR)\href.js" \ |
| 585 | 585 | "$(SRCDIR)\login.js" \ |
| 586 | 586 | "$(SRCDIR)\markdown.md" \ |
| 587 | 587 | "$(SRCDIR)\menu.js" \ |
| 588 | 588 | "$(SRCDIR)\sbsdiff.js" \ |
| @@ -1136,11 +1136,10 @@ | ||
| 1136 | 1136 | echo "$(SRCDIR)\../skins/bootstrap/header.txt" >> $@ |
| 1137 | 1137 | echo "$(SRCDIR)\../skins/default/css.txt" >> $@ |
| 1138 | 1138 | echo "$(SRCDIR)\../skins/default/details.txt" >> $@ |
| 1139 | 1139 | echo "$(SRCDIR)\../skins/default/footer.txt" >> $@ |
| 1140 | 1140 | echo "$(SRCDIR)\../skins/default/header.txt" >> $@ |
| 1141 | - echo "$(SRCDIR)\../skins/default/js.txt" >> $@ | |
| 1142 | 1141 | echo "$(SRCDIR)\../skins/eagle/css.txt" >> $@ |
| 1143 | 1142 | echo "$(SRCDIR)\../skins/eagle/details.txt" >> $@ |
| 1144 | 1143 | echo "$(SRCDIR)\../skins/eagle/footer.txt" >> $@ |
| 1145 | 1144 | echo "$(SRCDIR)\../skins/eagle/header.txt" >> $@ |
| 1146 | 1145 | echo "$(SRCDIR)\../skins/enhanced1/css.txt" >> $@ |
| @@ -1192,10 +1191,11 @@ | ||
| 1192 | 1191 | echo "$(SRCDIR)\fossil.popupwidget.js" >> $@ |
| 1193 | 1192 | echo "$(SRCDIR)\fossil.storage.js" >> $@ |
| 1194 | 1193 | echo "$(SRCDIR)\fossil.tabs.js" >> $@ |
| 1195 | 1194 | echo "$(SRCDIR)\fossil.wikiedit-wysiwyg.js" >> $@ |
| 1196 | 1195 | echo "$(SRCDIR)\graph.js" >> $@ |
| 1196 | + echo "$(SRCDIR)\hbmenu.js" >> $@ | |
| 1197 | 1197 | echo "$(SRCDIR)\href.js" >> $@ |
| 1198 | 1198 | echo "$(SRCDIR)\login.js" >> $@ |
| 1199 | 1199 | echo "$(SRCDIR)\markdown.md" >> $@ |
| 1200 | 1200 | echo "$(SRCDIR)\menu.js" >> $@ |
| 1201 | 1201 | echo "$(SRCDIR)\sbsdiff.js" >> $@ |
| 1202 | 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\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 |