dongjoon-hyun commented on code in PR #59547:
URL: https://github.com/apache/spark/pull/59547#discussion_r4318348888


##########
docs/_layouts/global.html:
##########
@@ -196,7 +196,18 @@ <h1 class="title">{{ page.title }}</h1>
         <script src="{{ rel_path_to_root }}js/vendor/jquery.min.js"></script>
         <script src="{{ rel_path_to_root 
}}js/vendor/bootstrap.bundle.min.js"></script>
 
-        <script src="{{ rel_path_to_root }}js/vendor/anchor.min.js"></script>
+        <script src="{{ rel_path_to_root }}js/vendor/anchor.min.js">
+            // Keep the version of AnchorJS used here in sync with what's used 
on the main website.
+            // See: 
https://github.com/apache/spark-website/blob/asf-site/js/vendor/anchor.min.js
+        </script>
+        <script>
+            // Display anchor links when hovering over headings.
+            // See: https://www.bryanbraun.com/anchorjs/
+            anchors.options = { placement: 'right' };
+            // Note: AnchorJS 4.0+ excludes h1 from the default selector, but 
many
+            // Spark doc pages use h1 for section headings, so we include it 
explicitly.
+            anchors.add('h1, h2, h3, h4, h5, h6');

Review Comment:
   3. Minor: moving this call out of `main.js` can briefly double the anchors 
right after a release. spark.apache.org serves `docs/latest/js/*.js` with only 
`Last-Modified` and `ETag`, without `Cache-Control` or `Expires`, so browsers 
may keep reusing cached copies for days after `docs/latest` moves to a new 
release. A returning visitor who still has the old `main.js` and the 1.1.1 
`anchor.min.js` cached runs this new `anchors.add(...)` first and the old 
`anchors.add()` in `main.js` second. 1.1.1 has no check for an existing anchor, 
so every heading shows two icons until the cache expires. It is cosmetic and 
temporary. Could we keep the call in `main.js` as in item 5, which avoids this 
mixed state?



##########
licenses/LICENSE-AnchorJS.txt:
##########
@@ -1,6 +1,6 @@
 The MIT License (MIT)
 
-Copyright (c) <year> <copyright holders>
+Copyright (c) 2023 Bryan Braun

Review Comment:
   **1. The fix does not reach the copy that ships.** 
`licenses-binary/LICENSE-AnchorJS.txt:3` still reads `Copyright (c) <year> 
<copyright holders>`, and that directory is what users get: 
`dev/make-distribution.sh:258` copies it into the binary tarball, and 
`python/packaging/classic/setup.py:108` packages it as `pyspark/licenses`. 
Before this PR, every file present in both `licenses/` and `licenses-binary/` 
was identical; now this is the only pair that differs. Since SPARK-48487 
removed `docs/js/vendor/anchor.min.js` from `LICENSE-binary`, the binary copy 
is an orphan, so removing it would also work.
   
   Separately, the upstream `LICENSE` at tag 5.0.0 says `Copyright (c) 2021 
Bryan Braun` under the title `MIT License`; 2023 is the year in the 
`anchor.min.js` banner. Either year is fine, but the commit message says "to 
match anchorjs upstream".
   
   Could you apply the same change to `licenses-binary/LICENSE-AnchorJS.txt`, 
or remove that file?



##########
docs/_layouts/global.html:
##########
@@ -196,7 +196,18 @@ <h1 class="title">{{ page.title }}</h1>
         <script src="{{ rel_path_to_root }}js/vendor/jquery.min.js"></script>
         <script src="{{ rel_path_to_root 
}}js/vendor/bootstrap.bundle.min.js"></script>
 
-        <script src="{{ rel_path_to_root }}js/vendor/anchor.min.js"></script>
+        <script src="{{ rel_path_to_root }}js/vendor/anchor.min.js">
+            // Keep the version of AnchorJS used here in sync with what's used 
on the main website.
+            // See: 
https://github.com/apache/spark-website/blob/asf-site/js/vendor/anchor.min.js
+        </script>
+        <script>
+            // Display anchor links when hovering over headings.
+            // See: https://www.bryanbraun.com/anchorjs/
+            anchors.options = { placement: 'right' };

Review Comment:
   5. Minor: moving these lines out of `docs/js/main.js` also takes them out of 
the lint check. `dev/lint-js` runs ESLint (`eslint:recommended`, which includes 
`no-undef`) on `docs/js` only, and nothing lints `docs/_layouts`. So a later 
typo here, such as `anchros.add(...)`, would pass CI and silently remove the 
anchors from every page, while the same typo in `main.js` fails `no-undef` 
(which is why `main.js` declared `/* global $, anchors */`). A separate ready 
handler in `main.js` keeps both the lint check and the isolation you get from a 
separate script, because jQuery 3.7.1 runs each ready handler in its own 
promise chain, so an error in one does not skip the dropdown setup in the other:
   
   ```js
   $(function() {
     anchors.options = { placement: 'right' };
     anchors.add('h1, h2, h3, h4, h5, h6');
   });
   ```
   
   Could you move the config back into `main.js` this way and restore `/* 
global $, anchors */`?



##########
docs/_layouts/global.html:
##########
@@ -196,7 +196,18 @@ <h1 class="title">{{ page.title }}</h1>
         <script src="{{ rel_path_to_root }}js/vendor/jquery.min.js"></script>
         <script src="{{ rel_path_to_root 
}}js/vendor/bootstrap.bundle.min.js"></script>
 
-        <script src="{{ rel_path_to_root }}js/vendor/anchor.min.js"></script>
+        <script src="{{ rel_path_to_root }}js/vendor/anchor.min.js">

Review Comment:
   6. Minor: browsers ignore the body of a `<script>` that has `src`. This is 
the only `<script src>` in the layout with a body (L30, L196, L197, L211, and 
L213 are empty), so a later edit that puts real code here would silently never 
run. The note also ships in all 248 generated pages, and the link returns 404 
until apache/spark-website#700 is merged. Could you close this tag empty and 
move the note into a Jekyll `{% comment %}` block, as you did in 
`docs/sql-error-conditions.md` for SPARK-46935, or into a `//` line in the 
inline script below? apache/spark-website#700 uses the same pattern.



##########
docs/_layouts/global.html:
##########
@@ -196,7 +196,18 @@ <h1 class="title">{{ page.title }}</h1>
         <script src="{{ rel_path_to_root }}js/vendor/jquery.min.js"></script>
         <script src="{{ rel_path_to_root 
}}js/vendor/bootstrap.bundle.min.js"></script>
 
-        <script src="{{ rel_path_to_root }}js/vendor/anchor.min.js"></script>
+        <script src="{{ rel_path_to_root }}js/vendor/anchor.min.js">
+            // Keep the version of AnchorJS used here in sync with what's used 
on the main website.
+            // See: 
https://github.com/apache/spark-website/blob/asf-site/js/vendor/anchor.min.js
+        </script>
+        <script>
+            // Display anchor links when hovering over headings.
+            // See: https://www.bryanbraun.com/anchorjs/
+            anchors.options = { placement: 'right' };
+            // Note: AnchorJS 4.0+ excludes h1 from the default selector, but 
many
+            // Spark doc pages use h1 for section headings, so we include it 
explicitly.
+            anchors.add('h1, h2, h3, h4, h5, h6');

Review Comment:
   **2. This selector also anchors the layout's own headings, not just the page 
content.** It matches the fixed sidebar titles from 
`_includes/nav-left-wrapper-*.html` (for example `<h3><a 
href="sql-programming-guide.html">Spark SQL Guide</a></h3>`) and the hero 
`<h1>` on the landing page. As a result:
   
   - On the 151 `sql*.html` pages, the sidebar title gets an anchor to 
`#spark-sql-guide` on the current page, and screen readers read one more 
"Anchor" link there.
   - On the 7 streaming pages titled "Structured Streaming Programming Guide", 
the sidebar `<h3>` comes first in DOM order, so it takes 
`#structured-streaming-programming-guide`, and the real page title gets 
`#structured-streaming-programming-guide-1`.
   
   This is not a regression, because 1.1.1's default selector matched the same 
elements. But since this line is being rewritten anyway, could we scope it to 
the content? Both layout branches use `id="content"`, so `anchors.add('#content 
h1, #content h2, #content h3, #content h4, #content h5, #content h6');` keeps 
the page-title and section anchors and drops the sidebar and hero ones.



##########
docs/js/main.js:
##########
@@ -89,12 +89,6 @@ function codeTabs() {
 
 $(function() {
   codeTabs();
-  // Display anchor links when hovering over headers. For documentation of the
-  // configuration options, see the AnchorJS documentation.
-  anchors.options = {
-    placement: 'right'
-  };
-  anchors.add();
 
   // Make dropdown menus in nav bars show on hover instead of click

Review Comment:
   11. Nit: this is pre-existing and outside this PR. The dropdown block below 
(L100-L119) calls `$dropdown.hover(...)` again on every `load` or `resize` 
event at 768px or wider, without unbinding the previous handlers. jQuery's 
`hover()` is just `.on("mouseenter", ...).on("mouseleave", ...)`, so the 
handlers pile up until the window becomes narrower than 768px. They are 
idempotent, so this only wastes work. The 768px threshold also differs from the 
navbar's `navbar-expand-lg` breakpoint (992px). This dates from SPARK-30654. 
Could `$dropdown.off("mouseenter mouseleave").hover(...)` go into a small 
follow-up?



##########
docs/js/vendor/anchor.min.js:
##########
@@ -1,6 +1,9 @@
-/*!
- * AnchorJS - v1.1.1 - 2015-05-23
- * https://github.com/bryanbraun/anchorjs
- * Copyright (c) 2015 Bryan Braun; Licensed MIT
- */
-function AnchorJS(A){"use 
strict";this.options=A||{},this._applyRemainingDefaultOptions=function(A){this.options.icon=this.options.hasOwnProperty("icon")?A.icon:"&#xe9cb",this.options.visible=this.options.hasOwnProperty("visible")?A.visible:"hover",this.options.placement=this.options.hasOwnProperty("placement")?A.placement:"right",this.options.class=this.options.hasOwnProperty("class")?A.class:""},this._applyRemainingDefaultOptions(A),this.add=function(A){var
 
e,t,o,n,i,s,a,l,c,r,h,g,B,Q;if(this._applyRemainingDefaultOptions(this.options),A){if("string"!=typeof
 A)throw new Error("The selector provided to AnchorJS was invalid.")}else 
A="h1, h2, h3, h4, h5, 
h6";if(e=document.querySelectorAll(A),0===e.length)return!1;for(this._addBaselineStyles(),t=document.querySelectorAll("[id]"),o=[].map.call(t,function(A){return
 
A.id}),i=0;i<e.length;i++){if(e[i].hasAttribute("id"))n=e[i].getAttribute("id");else{s=e[i].textContent,a=s.replace(/[^\w\s-]/gi,"").replace(/\s+/g,"-").replace(/-{2,}/g,"-"
 ).substring(0,64).replace(/^-+|-+$/gm,"").toLowerCase(),r=a,c=0;do void 
0!==l&&(r=a+"-"+c),l=o.indexOf(r),c+=1;while(-1!==l);l=void 
0,o.push(r),e[i].setAttribute("id",r),n=r}h=n.replace(/-/g," "),g='<a 
class="anchorjs-link '+this.options.class+'" href="#'+n+'" aria-label="Anchor 
link for: '+h+'" 
data-anchorjs-icon="'+this.options.icon+'"></a>',B=document.createElement("div"),B.innerHTML=g,Q=B.childNodes,"always"===this.options.visible&&(Q[0].style.opacity="1"),"&#xe9cb"===this.options.icon&&(Q[0].style.fontFamily="anchorjs-icons",Q[0].style.fontStyle="normal",Q[0].style.fontVariant="normal",Q[0].style.fontWeight="normal"),"left"===this.options.placement?(Q[0].style.position="absolute",Q[0].style.marginLeft="-1em",Q[0].style.paddingRight="0.5em",e[i].insertBefore(Q[0],e[i].firstChild)):(Q[0].style.paddingLeft="0.375em",e[i].appendChild(Q[0]))}return
 this},this.remove=function(A){for(var 
e,t=document.querySelectorAll(A),o=0;o<t.length;o++)e=t[o].querySelector(".anchorjs-link"),e&&t[o]
 .removeChild(e);return 
this},this._addBaselineStyles=function(){if(null===document.head.querySelector("style.anchorjs")){var
 A,e=document.createElement("style"),t=" .anchorjs-link {   opacity: 0;   
text-decoration: none;   -webkit-font-smoothing: antialiased;   
-moz-osx-font-smoothing: grayscale; }",o=" *:hover > .anchorjs-link, 
.anchorjs-link:focus  {   opacity: 1; }",n=' @font-face {   font-family: 
"anchorjs-icons";   font-style: normal;   font-weight: normal;   src: 
url(data:application/x-font-ttf;charset=utf-8;base64,AAEAAAALAIAAAwAwT1MvMg8SBTUAAAC8AAAAYGNtYXAWi9QdAAABHAAAAFRnYXNwAAAAEAAAAXAAAAAIZ2x5Zgq29TcAAAF4AAABNGhlYWQEZM3pAAACrAAAADZoaGVhBhUDxgAAAuQAAAAkaG10eASAADEAAAMIAAAAFGxvY2EAKACuAAADHAAAAAxtYXhwAAgAVwAAAygAAAAgbmFtZQ5yJ3cAAANIAAAB2nBvc3QAAwAAAAAFJAAAACAAAwJAAZAABQAAApkCzAAAAI8CmQLMAAAB6wAzAQkAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAABAAADpywPA/8AAQAPAAEAAAAABAAAAAAAAAAAAAAAgAAAAAAADAAAAAwAAABwAAQADAAAAHAADAAEAAAAcAAQAOAAAAAoACAACAAIAAQAg6cv//f//AAAAAAAg6cv//f//AAH/4x
 
Y5AAMAAQAAAAAAAAAAAAAAAQAB//8ADwABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAAAQAAAAAAAAAAAAIAADc5AQAAAAACADEARAJTAsAAKwBUAAABIiYnJjQ/AT4BMzIWFxYUDwEGIicmND8BNjQnLgEjIgYPAQYUFxYUBw4BIwciJicmND8BNjIXFhQPAQYUFx4BMzI2PwE2NCcmNDc2MhcWFA8BDgEjARQGDAUtLXoWOR8fORYtLTgKGwoKCjgaGg0gEhIgDXoaGgkJBQwHdR85Fi0tOAobCgoKOBoaDSASEiANehoaCQkKGwotLXoWOR8BMwUFLYEuehYXFxYugC44CQkKGwo4GkoaDQ0NDXoaShoKGwoFBe8XFi6ALjgJCQobCjgaShoNDQ0NehpKGgobCgoKLYEuehYXAAEAAAABAACiToc1Xw889QALBAAAAAAA0XnFFgAAAADRecUWAAAAAAJTAsAAAAAIAAIAAAAAAAAAAQAAA8D/wAAABAAAAAAAAlMAAQAAAAAAAAAAAAAAAAAAAAUAAAAAAAAAAAAAAAACAAAAAoAAMQAAAAAACgAUAB4AmgABAAAABQBVAAIAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAADgCuAAEAAAAAAAEADgAAAAEAAAAAAAIABwCfAAEAAAAAAAMADgBLAAEAAAAAAAQADgC0AAEAAAAAAAUACwAqAAEAAAAAAAYADgB1AAEAAAAAAAoAGgDeAAMAAQQJAAEAHAAOAAMAAQQJAAIADgCmAAMAAQQJAAMAHABZAAMAAQQJAAQAHADCAAMAAQQJAAUAFgA1AAMAAQQJAAYAHACDAAMAAQQJAAoANAD4YW5jaG9yanMtaWNvbnMAYQBuAGMAaABvAHIAagBzAC0AaQBjAG8AbgBzVmVyc2lvbiAxLjAAVgBlAHIAcwBpAG8AbgAgADEALgAwYW5jaG9yanMtaWN
 
vbnMAYQBuAGMAaABvAHIAagBzAC0AaQBjAG8AbgBzYW5jaG9yanMtaWNvbnMAYQBuAGMAaABvAHIAagBzAC0AaQBjAG8AbgBzUmVndWxhcgBSAGUAZwB1AGwAYQByYW5jaG9yanMtaWNvbnMAYQBuAGMAaABvAHIAagBzAC0AaQBjAG8AbgBzRm9udCBnZW5lcmF0ZWQgYnkgSWNvTW9vbi4ARgBvAG4AdAAgAGcAZQBuAGUAcgBhAHQAZQBkACAAYgB5ACAASQBjAG8ATQBvAG8AbgAuAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==)
 format("truetype"); }',i=" [data-anchorjs-icon]::after {   content: 
attr(data-anchorjs-icon); 
}";e.className="anchorjs",e.appendChild(document.createTextNode("")),A=document.head.querySelector('[rel="stylesheet"],
 style'),void 
0===A?document.head.appendChild(e):document.head.insertBefore(e,A),e.sheet.insertRule(t,e.sheet.cssRules.length),e.sheet.insertRule(o,e.sheet.cssRules.length),e.sheet.insertRule(i,e.sheet.cssRules.length),e.sheet.insertRule(n,e.sheet.cssRules.length)}}}var
 anchors=new AnchorJS;
+// @license 
magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt Expat
+//
+// AnchorJS - v5.0.0 - 2023-01-18
+// https://www.bryanbraun.com/anchorjs/
+// Copyright (c) 2023 Bryan Braun; Licensed MIT
+//
+// @license 
magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt Expat
+!function(A,e){"use strict";"function"==typeof 
define&&define.amd?define([],e):"object"==typeof 
module&&module.exports?module.exports=e():(A.AnchorJS=e(),A.anchors=new 
A.AnchorJS)}(globalThis,function(){"use strict";return function(A){function 
u(A){A.icon=Object.prototype.hasOwnProperty.call(A,"icon")?A.icon:"",A.visible=Object.prototype.hasOwnProperty.call(A,"visible")?A.visible:"hover",A.placement=Object.prototype.hasOwnProperty.call(A,"placement")?A.placement:"right",A.ariaLabel=Object.prototype.hasOwnProperty.call(A,"ariaLabel")?A.ariaLabel:"Anchor",A.class=Object.prototype.hasOwnProperty.call(A,"class")?A.class:"",A.base=Object.prototype.hasOwnProperty.call(A,"base")?A.base:"",A.truncate=Object.prototype.hasOwnProperty.call(A,"truncate")?Math.floor(A.truncate):64,A.titleText=Object.prototype.hasOwnProperty.call(A,"titleText")?A.titleText:""}function
 d(A){var e;if("string"==typeof A||A instanceof 
String)e=[].slice.call(document.querySelectorAll(A));else{if(!(Array.isArray(A)|
 |A instanceof NodeList))throw new TypeError("The selector provided to AnchorJS 
was invalid.");e=[].slice.call(A)}return 
e}this.options=A||{},this.elements=[],u(this.options),this.add=function(A){var 
e,t,o,i,n,s,a,r,l,c,h,p=[];if(u(this.options),0!==(e=d(A=A||"h2, h3, h4, h5, 
h6")).length){for(null===document.head.querySelector("style.anchorjs")&&((A=document.createElement("style")).className="anchorjs",A.appendChild(document.createTextNode("")),void
 
0===(h=document.head.querySelector('[rel="stylesheet"],style'))?document.head.appendChild(A):document.head.insertBefore(A,h),A.sheet.insertRule(".anchorjs-link{opacity:0;text-decoration:none;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}",A.sheet.cssRules.length),A.sheet.insertRule(":hover>.anchorjs-link,.anchorjs-link:focus{opacity:1}",A.sheet.cssRules.length),A.sheet.insertRule("[data-anchorjs-icon]::after{content:attr(data-anchorjs-icon)}",A.sheet.cssRules.length),A.sheet.insertRule('@font-face{font-family:ancho
 
rjs-icons;src:url(data:n/a;base64,AAEAAAALAIAAAwAwT1MvMg8yG2cAAAE4AAAAYGNtYXDp3gC3AAABpAAAAExnYXNwAAAAEAAAA9wAAAAIZ2x5ZlQCcfwAAAH4AAABCGhlYWQHFvHyAAAAvAAAADZoaGVhBnACFwAAAPQAAAAkaG10eASAADEAAAGYAAAADGxvY2EACACEAAAB8AAAAAhtYXhwAAYAVwAAARgAAAAgbmFtZQGOH9cAAAMAAAAAunBvc3QAAwAAAAADvAAAACAAAQAAAAEAAHzE2p9fDzz1AAkEAAAAAADRecUWAAAAANQA6R8AAAAAAoACwAAAAAgAAgAAAAAAAAABAAADwP/AAAACgAAA/9MCrQABAAAAAAAAAAAAAAAAAAAAAwABAAAAAwBVAAIAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAMCQAGQAAUAAAKZAswAAACPApkCzAAAAesAMwEJAAAAAAAAAAAAAAAAAAAAARAAAAAAAAAAAAAAAAAAAAAAQAAg//0DwP/AAEADwABAAAAAAQAAAAAAAAAAAAAAIAAAAAAAAAIAAAACgAAxAAAAAwAAAAMAAAAcAAEAAwAAABwAAwABAAAAHAAEADAAAAAIAAgAAgAAACDpy//9//8AAAAg6cv//f///+EWNwADAAEAAAAAAAAAAAAAAAAACACEAAEAAAAAAAAAAAAAAAAxAAACAAQARAKAAsAAKwBUAAABIiYnJjQ3NzY2MzIWFxYUBwcGIicmNDc3NjQnJiYjIgYHBwYUFxYUBwYGIwciJicmNDc3NjIXFhQHBwYUFxYWMzI2Nzc2NCcmNDc2MhcWFAcHBgYjARQGDAUtLXoWOR8fORYtLTgKGwoKCjgaGg0gEhIgDXoaGgkJBQwHdR85Fi0tOAobCgoKOBoaDSASEiANehoaCQkKGwotLXoWOR8BMwUFLYEuehYXFxYugC44CQkKGwo4GkoaDQ0
 
NDXoaShoKGwoFBe8XFi6ALjgJCQobCjgaShoNDQ0NehpKGgobCgoKLYEuehYXAAAADACWAAEAAAAAAAEACAAAAAEAAAAAAAIAAwAIAAEAAAAAAAMACAAAAAEAAAAAAAQACAAAAAEAAAAAAAUAAQALAAEAAAAAAAYACAAAAAMAAQQJAAEAEAAMAAMAAQQJAAIABgAcAAMAAQQJAAMAEAAMAAMAAQQJAAQAEAAMAAMAAQQJAAUAAgAiAAMAAQQJAAYAEAAMYW5jaG9yanM0MDBAAGEAbgBjAGgAbwByAGoAcwA0ADAAMABAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAH//wAP)
 
format("truetype")}',A.sheet.cssRules.length)),h=document.querySelectorAll("[id]"),t=[].map.call(h,function(A){return
 
A.id}),i=0;i<e.length;i++)if(this.hasAnchorJSLink(e[i]))p.push(i);else{if(e[i].hasAttribute("id"))o=e[i].getAttribute("id");else
 
if(e[i].hasAttribute("data-anchor-id"))o=e[i].getAttribute("data-anchor-id");else{for(r=a=this.urlify(e[i].textContent),s=0;n=t.indexOf(r=void
 0!==n?a+"-"+s:r),s+=1,-1!==n;);n=void 
0,t.push(r),e[i].setAttribute("id",r),o=r}(l=document.createElement("a")).className="anchorjs-link
 
"+this.options.class,l.setAttribute("aria-label",this.options.ariaLabel),l.setAttribute("data-anchorjs-
 
icon",this.options.icon),this.options.titleText&&(l.title=this.options.titleText),c=document.querySelector("base")?window.location.pathname+window.location.search:"",c=this.options.base||c,l.href=c+"#"+o,"always"===this.options.visible&&(l.style.opacity="1"),""===this.options.icon&&(l.style.font="1em/1
 
anchorjs-icons","left"===this.options.placement)&&(l.style.lineHeight="inherit"),"left"===this.options.placement?(l.style.position="absolute",l.style.marginLeft="-1.25em",l.style.paddingRight=".25em",l.style.paddingLeft=".25em",e[i].insertBefore(l,e[i].firstChild)):(l.style.marginLeft=".1875em",l.style.paddingRight=".1875em",l.style.paddingLeft=".1875em",e[i].appendChild(l))}for(i=0;i<p.length;i++)e.splice(p[i]-i,1);this.elements=this.elements.concat(e)}return
 this},this.remove=function(A){for(var 
e,t,o=d(A),i=0;i<o.length;i++)(t=o[i].querySelector(".anchorjs-link"))&&(-1!==(e=this.elements.indexOf(o[i]))&&this.elements.splice(e,1),o[i].removeChild(t));return
 this},this.removeAll=f
 unction(){this.remove(this.elements)},this.urlify=function(A){var 
e=document.createElement("textarea");return 
e.innerHTML=A,A=e.value,this.options.truncate||u(this.options),A.trim().replace(/'/gi,"").replace(/[&
 
+$,:;=?@"#{}|^~[`%!'<>\]./()*\\\n\t\b\v\u00A0]/g,"-").replace(/-{2,}/g,"-").substring(0,this.options.truncate).replace(/^-+|-+$/gm,"").toLowerCase()},this.hasAnchorJSLink=function(A){var
 e=A.firstChild&&-1<(" "+A.firstChild.className+" ").indexOf(" anchorjs-link 
"),A=A.lastChild&&-1<(" "+A.lastChild.className+" ").indexOf(" anchorjs-link 
");return e||A||!1}}});

Review Comment:
   4. Minor: 5.0.0 passes a bare `globalThis` to its UMD wrapper here, unlike 
1.1.1 (plain ES5) and our Bootstrap 5.0.2 bundle (which checks `typeof 
globalThis`). On Chrome < 71, Firefox < 65 (including ESR 60), and Safari < 
12.1, which Bootstrap 5.0.2's `.browserslistrc` still lists, this line throws 
`ReferenceError: globalThis is not defined`. Then `global.html:206` throws 
`anchors is not defined`, and those browsers get no heading anchors at all. The 
rest of the page still works, because the config runs in its own `<script>`. 
These browsers have been out of support for years, so accepting this is fine 
with me. Could you mention it in the PR description, or add `if (typeof 
globalThis === 'undefined') { window.globalThis = window; }` before this script 
if you want to keep them working?



##########
docs/_layouts/global.html:
##########
@@ -196,7 +196,18 @@ <h1 class="title">{{ page.title }}</h1>
         <script src="{{ rel_path_to_root }}js/vendor/jquery.min.js"></script>
         <script src="{{ rel_path_to_root 
}}js/vendor/bootstrap.bundle.min.js"></script>
 
-        <script src="{{ rel_path_to_root }}js/vendor/anchor.min.js"></script>
+        <script src="{{ rel_path_to_root }}js/vendor/anchor.min.js">
+            // Keep the version of AnchorJS used here in sync with what's used 
on the main website.
+            // See: 
https://github.com/apache/spark-website/blob/asf-site/js/vendor/anchor.min.js
+        </script>
+        <script>
+            // Display anchor links when hovering over headings.
+            // See: https://www.bryanbraun.com/anchorjs/
+            anchors.options = { placement: 'right' };

Review Comment:
   9. Nit: `placement: 'right'` is already the default in both 1.1.1 and 5.0.0, 
and `add()` re-applies the defaults, so this line has no effect. It has been a 
no-op since SPARK-12324 changed it from `'left'` in 2015. Keeping it as an 
explicit choice is fine too. Could you drop it, here and in 
apache/spark-website#700, if you prefer the shorter form?



##########
docs/js/vendor/anchor.min.js:
##########
@@ -1,6 +1,9 @@
-/*!
- * AnchorJS - v1.1.1 - 2015-05-23
- * https://github.com/bryanbraun/anchorjs
- * Copyright (c) 2015 Bryan Braun; Licensed MIT
- */
-function AnchorJS(A){"use 
strict";this.options=A||{},this._applyRemainingDefaultOptions=function(A){this.options.icon=this.options.hasOwnProperty("icon")?A.icon:"&#xe9cb",this.options.visible=this.options.hasOwnProperty("visible")?A.visible:"hover",this.options.placement=this.options.hasOwnProperty("placement")?A.placement:"right",this.options.class=this.options.hasOwnProperty("class")?A.class:""},this._applyRemainingDefaultOptions(A),this.add=function(A){var
 
e,t,o,n,i,s,a,l,c,r,h,g,B,Q;if(this._applyRemainingDefaultOptions(this.options),A){if("string"!=typeof
 A)throw new Error("The selector provided to AnchorJS was invalid.")}else 
A="h1, h2, h3, h4, h5, 
h6";if(e=document.querySelectorAll(A),0===e.length)return!1;for(this._addBaselineStyles(),t=document.querySelectorAll("[id]"),o=[].map.call(t,function(A){return
 
A.id}),i=0;i<e.length;i++){if(e[i].hasAttribute("id"))n=e[i].getAttribute("id");else{s=e[i].textContent,a=s.replace(/[^\w\s-]/gi,"").replace(/\s+/g,"-").replace(/-{2,}/g,"-"
 ).substring(0,64).replace(/^-+|-+$/gm,"").toLowerCase(),r=a,c=0;do void 
0!==l&&(r=a+"-"+c),l=o.indexOf(r),c+=1;while(-1!==l);l=void 
0,o.push(r),e[i].setAttribute("id",r),n=r}h=n.replace(/-/g," "),g='<a 
class="anchorjs-link '+this.options.class+'" href="#'+n+'" aria-label="Anchor 
link for: '+h+'" 
data-anchorjs-icon="'+this.options.icon+'"></a>',B=document.createElement("div"),B.innerHTML=g,Q=B.childNodes,"always"===this.options.visible&&(Q[0].style.opacity="1"),"&#xe9cb"===this.options.icon&&(Q[0].style.fontFamily="anchorjs-icons",Q[0].style.fontStyle="normal",Q[0].style.fontVariant="normal",Q[0].style.fontWeight="normal"),"left"===this.options.placement?(Q[0].style.position="absolute",Q[0].style.marginLeft="-1em",Q[0].style.paddingRight="0.5em",e[i].insertBefore(Q[0],e[i].firstChild)):(Q[0].style.paddingLeft="0.375em",e[i].appendChild(Q[0]))}return
 this},this.remove=function(A){for(var 
e,t=document.querySelectorAll(A),o=0;o<t.length;o++)e=t[o].querySelector(".anchorjs-link"),e&&t[o]
 .removeChild(e);return 
this},this._addBaselineStyles=function(){if(null===document.head.querySelector("style.anchorjs")){var
 A,e=document.createElement("style"),t=" .anchorjs-link {   opacity: 0;   
text-decoration: none;   -webkit-font-smoothing: antialiased;   
-moz-osx-font-smoothing: grayscale; }",o=" *:hover > .anchorjs-link, 
.anchorjs-link:focus  {   opacity: 1; }",n=' @font-face {   font-family: 
"anchorjs-icons";   font-style: normal;   font-weight: normal;   src: 
url(data:application/x-font-ttf;charset=utf-8;base64,AAEAAAALAIAAAwAwT1MvMg8SBTUAAAC8AAAAYGNtYXAWi9QdAAABHAAAAFRnYXNwAAAAEAAAAXAAAAAIZ2x5Zgq29TcAAAF4AAABNGhlYWQEZM3pAAACrAAAADZoaGVhBhUDxgAAAuQAAAAkaG10eASAADEAAAMIAAAAFGxvY2EAKACuAAADHAAAAAxtYXhwAAgAVwAAAygAAAAgbmFtZQ5yJ3cAAANIAAAB2nBvc3QAAwAAAAAFJAAAACAAAwJAAZAABQAAApkCzAAAAI8CmQLMAAAB6wAzAQkAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAABAAADpywPA/8AAQAPAAEAAAAABAAAAAAAAAAAAAAAgAAAAAAADAAAAAwAAABwAAQADAAAAHAADAAEAAAAcAAQAOAAAAAoACAACAAIAAQAg6cv//f//AAAAAAAg6cv//f//AAH/4x
 
Y5AAMAAQAAAAAAAAAAAAAAAQAB//8ADwABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAAAQAAAAAAAAAAAAIAADc5AQAAAAACADEARAJTAsAAKwBUAAABIiYnJjQ/AT4BMzIWFxYUDwEGIicmND8BNjQnLgEjIgYPAQYUFxYUBw4BIwciJicmND8BNjIXFhQPAQYUFx4BMzI2PwE2NCcmNDc2MhcWFA8BDgEjARQGDAUtLXoWOR8fORYtLTgKGwoKCjgaGg0gEhIgDXoaGgkJBQwHdR85Fi0tOAobCgoKOBoaDSASEiANehoaCQkKGwotLXoWOR8BMwUFLYEuehYXFxYugC44CQkKGwo4GkoaDQ0NDXoaShoKGwoFBe8XFi6ALjgJCQobCjgaShoNDQ0NehpKGgobCgoKLYEuehYXAAEAAAABAACiToc1Xw889QALBAAAAAAA0XnFFgAAAADRecUWAAAAAAJTAsAAAAAIAAIAAAAAAAAAAQAAA8D/wAAABAAAAAAAAlMAAQAAAAAAAAAAAAAAAAAAAAUAAAAAAAAAAAAAAAACAAAAAoAAMQAAAAAACgAUAB4AmgABAAAABQBVAAIAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAADgCuAAEAAAAAAAEADgAAAAEAAAAAAAIABwCfAAEAAAAAAAMADgBLAAEAAAAAAAQADgC0AAEAAAAAAAUACwAqAAEAAAAAAAYADgB1AAEAAAAAAAoAGgDeAAMAAQQJAAEAHAAOAAMAAQQJAAIADgCmAAMAAQQJAAMAHABZAAMAAQQJAAQAHADCAAMAAQQJAAUAFgA1AAMAAQQJAAYAHACDAAMAAQQJAAoANAD4YW5jaG9yanMtaWNvbnMAYQBuAGMAaABvAHIAagBzAC0AaQBjAG8AbgBzVmVyc2lvbiAxLjAAVgBlAHIAcwBpAG8AbgAgADEALgAwYW5jaG9yanMtaWN
 
vbnMAYQBuAGMAaABvAHIAagBzAC0AaQBjAG8AbgBzYW5jaG9yanMtaWNvbnMAYQBuAGMAaABvAHIAagBzAC0AaQBjAG8AbgBzUmVndWxhcgBSAGUAZwB1AGwAYQByYW5jaG9yanMtaWNvbnMAYQBuAGMAaABvAHIAagBzAC0AaQBjAG8AbgBzRm9udCBnZW5lcmF0ZWQgYnkgSWNvTW9vbi4ARgBvAG4AdAAgAGcAZQBuAGUAcgBhAHQAZQBkACAAYgB5ACAASQBjAG8ATQBvAG8AbgAuAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==)
 format("truetype"); }',i=" [data-anchorjs-icon]::after {   content: 
attr(data-anchorjs-icon); 
}";e.className="anchorjs",e.appendChild(document.createTextNode("")),A=document.head.querySelector('[rel="stylesheet"],
 style'),void 
0===A?document.head.appendChild(e):document.head.insertBefore(e,A),e.sheet.insertRule(t,e.sheet.cssRules.length),e.sheet.insertRule(o,e.sheet.cssRules.length),e.sheet.insertRule(i,e.sheet.cssRules.length),e.sheet.insertRule(n,e.sheet.cssRules.length)}}}var
 anchors=new AnchorJS;
+// @license 
magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt Expat
+//
+// AnchorJS - v5.0.0 - 2023-01-18
+// https://www.bryanbraun.com/anchorjs/
+// Copyright (c) 2023 Bryan Braun; Licensed MIT
+//
+// @license 
magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt Expat
+!function(A,e){"use strict";"function"==typeof 
define&&define.amd?define([],e):"object"==typeof 
module&&module.exports?module.exports=e():(A.AnchorJS=e(),A.anchors=new 
A.AnchorJS)}(globalThis,function(){"use strict";return function(A){function 
u(A){A.icon=Object.prototype.hasOwnProperty.call(A,"icon")?A.icon:"",A.visible=Object.prototype.hasOwnProperty.call(A,"visible")?A.visible:"hover",A.placement=Object.prototype.hasOwnProperty.call(A,"placement")?A.placement:"right",A.ariaLabel=Object.prototype.hasOwnProperty.call(A,"ariaLabel")?A.ariaLabel:"Anchor",A.class=Object.prototype.hasOwnProperty.call(A,"class")?A.class:"",A.base=Object.prototype.hasOwnProperty.call(A,"base")?A.base:"",A.truncate=Object.prototype.hasOwnProperty.call(A,"truncate")?Math.floor(A.truncate):64,A.titleText=Object.prototype.hasOwnProperty.call(A,"titleText")?A.titleText:""}function
 d(A){var e;if("string"==typeof A||A instanceof 
String)e=[].slice.call(document.querySelectorAll(A));else{if(!(Array.isArray(A)|
 |A instanceof NodeList))throw new TypeError("The selector provided to AnchorJS 
was invalid.");e=[].slice.call(A)}return 
e}this.options=A||{},this.elements=[],u(this.options),this.add=function(A){var 
e,t,o,i,n,s,a,r,l,c,h,p=[];if(u(this.options),0!==(e=d(A=A||"h2, h3, h4, h5, 
h6")).length){for(null===document.head.querySelector("style.anchorjs")&&((A=document.createElement("style")).className="anchorjs",A.appendChild(document.createTextNode("")),void
 
0===(h=document.head.querySelector('[rel="stylesheet"],style'))?document.head.appendChild(A):document.head.insertBefore(A,h),A.sheet.insertRule(".anchorjs-link{opacity:0;text-decoration:none;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}",A.sheet.cssRules.length),A.sheet.insertRule(":hover>.anchorjs-link,.anchorjs-link:focus{opacity:1}",A.sheet.cssRules.length),A.sheet.insertRule("[data-anchorjs-icon]::after{content:attr(data-anchorjs-icon)}",A.sheet.cssRules.length),A.sheet.insertRule('@font-face{font-family:ancho
 
rjs-icons;src:url(data:n/a;base64,AAEAAAALAIAAAwAwT1MvMg8yG2cAAAE4AAAAYGNtYXDp3gC3AAABpAAAAExnYXNwAAAAEAAAA9wAAAAIZ2x5ZlQCcfwAAAH4AAABCGhlYWQHFvHyAAAAvAAAADZoaGVhBnACFwAAAPQAAAAkaG10eASAADEAAAGYAAAADGxvY2EACACEAAAB8AAAAAhtYXhwAAYAVwAAARgAAAAgbmFtZQGOH9cAAAMAAAAAunBvc3QAAwAAAAADvAAAACAAAQAAAAEAAHzE2p9fDzz1AAkEAAAAAADRecUWAAAAANQA6R8AAAAAAoACwAAAAAgAAgAAAAAAAAABAAADwP/AAAACgAAA/9MCrQABAAAAAAAAAAAAAAAAAAAAAwABAAAAAwBVAAIAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAMCQAGQAAUAAAKZAswAAACPApkCzAAAAesAMwEJAAAAAAAAAAAAAAAAAAAAARAAAAAAAAAAAAAAAAAAAAAAQAAg//0DwP/AAEADwABAAAAAAQAAAAAAAAAAAAAAIAAAAAAAAAIAAAACgAAxAAAAAwAAAAMAAAAcAAEAAwAAABwAAwABAAAAHAAEADAAAAAIAAgAAgAAACDpy//9//8AAAAg6cv//f///+EWNwADAAEAAAAAAAAAAAAAAAAACACEAAEAAAAAAAAAAAAAAAAxAAACAAQARAKAAsAAKwBUAAABIiYnJjQ3NzY2MzIWFxYUBwcGIicmNDc3NjQnJiYjIgYHBwYUFxYUBwYGIwciJicmNDc3NjIXFhQHBwYUFxYWMzI2Nzc2NCcmNDc2MhcWFAcHBgYjARQGDAUtLXoWOR8fORYtLTgKGwoKCjgaGg0gEhIgDXoaGgkJBQwHdR85Fi0tOAobCgoKOBoaDSASEiANehoaCQkKGwotLXoWOR8BMwUFLYEuehYXFxYugC44CQkKGwo4GkoaDQ0
 
NDXoaShoKGwoFBe8XFi6ALjgJCQobCjgaShoNDQ0NehpKGgobCgoKLYEuehYXAAAADACWAAEAAAAAAAEACAAAAAEAAAAAAAIAAwAIAAEAAAAAAAMACAAAAAEAAAAAAAQACAAAAAEAAAAAAAUAAQALAAEAAAAAAAYACAAAAAMAAQQJAAEAEAAMAAMAAQQJAAIABgAcAAMAAQQJAAMAEAAMAAMAAQQJAAQAEAAMAAMAAQQJAAUAAgAiAAMAAQQJAAYAEAAMYW5jaG9yanM0MDBAAGEAbgBjAGgAbwByAGoAcwA0ADAAMABAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAH//wAP)
 
format("truetype")}',A.sheet.cssRules.length)),h=document.querySelectorAll("[id]"),t=[].map.call(h,function(A){return
 
A.id}),i=0;i<e.length;i++)if(this.hasAnchorJSLink(e[i]))p.push(i);else{if(e[i].hasAttribute("id"))o=e[i].getAttribute("id");else
 
if(e[i].hasAttribute("data-anchor-id"))o=e[i].getAttribute("data-anchor-id");else{for(r=a=this.urlify(e[i].textContent),s=0;n=t.indexOf(r=void
 0!==n?a+"-"+s:r),s+=1,-1!==n;);n=void 
0,t.push(r),e[i].setAttribute("id",r),o=r}(l=document.createElement("a")).className="anchorjs-link
 
"+this.options.class,l.setAttribute("aria-label",this.options.ariaLabel),l.setAttribute("data-anchorjs-
 
icon",this.options.icon),this.options.titleText&&(l.title=this.options.titleText),c=document.querySelector("base")?window.location.pathname+window.location.search:"",c=this.options.base||c,l.href=c+"#"+o,"always"===this.options.visible&&(l.style.opacity="1"),""===this.options.icon&&(l.style.font="1em/1
 
anchorjs-icons","left"===this.options.placement)&&(l.style.lineHeight="inherit"),"left"===this.options.placement?(l.style.position="absolute",l.style.marginLeft="-1.25em",l.style.paddingRight=".25em",l.style.paddingLeft=".25em",e[i].insertBefore(l,e[i].firstChild)):(l.style.marginLeft=".1875em",l.style.paddingRight=".1875em",l.style.paddingLeft=".1875em",e[i].appendChild(l))}for(i=0;i<p.length;i++)e.splice(p[i]-i,1);this.elements=this.elements.concat(e)}return
 this},this.remove=function(A){for(var 
e,t,o=d(A),i=0;i<o.length;i++)(t=o[i].querySelector(".anchorjs-link"))&&(-1!==(e=this.elements.indexOf(o[i]))&&this.elements.splice(e,1),o[i].removeChild(t));return
 this},this.removeAll=f
 unction(){this.remove(this.elements)},this.urlify=function(A){var 
e=document.createElement("textarea");return 
e.innerHTML=A,A=e.value,this.options.truncate||u(this.options),A.trim().replace(/'/gi,"").replace(/[&
 
+$,:;=?@"#{}|^~[`%!'<>\]./()*\\\n\t\b\v\u00A0]/g,"-").replace(/-{2,}/g,"-").substring(0,this.options.truncate).replace(/^-+|-+$/gm,"").toLowerCase()},this.hasAnchorJSLink=function(A){var
 e=A.firstChild&&-1<(" "+A.firstChild.className+" ").indexOf(" anchorjs-link 
"),A=A.lastChild&&-1<(" "+A.lastChild.className+" ").indexOf(" anchorjs-link 
");return e||A||!1}}});

Review Comment:
   10. Nit: FYI, 5.0.0's `add()` calls `document.querySelector('base')` inside 
its per-heading loop (`anchor.js` L140 at tag 5.0.0). Our pages have no 
`<base>`, so each heading triggers a scan of the whole document. On the largest 
page, `sql-ref-functions-builtin.html` (about 1.5 MB, ~48K elements, 48 
headings), that is about 48 extra scans during parsing. I have not measured it, 
but it should be a few milliseconds. Nothing to change here, since this file 
should stay identical to upstream. Could it be worth an upstream issue to move 
that query out of the loop?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to