1/* global addClass, hasClass, removeClass, onEachLazy */ 2 3"use strict"; 4 5(function() { 6 // Number of lines shown when code viewer is not expanded. 7 // DEFAULT is the first example shown by default, while HIDDEN is 8 // the examples hidden beneath the "More examples" toggle. 9 // 10 // NOTE: these values MUST be synchronized with certain rules in rustdoc.css! 11 const DEFAULT_MAX_LINES = 5; 12 const HIDDEN_MAX_LINES = 10; 13 14 // Scroll code block to the given code location 15 function scrollToLoc(elt, loc, isHidden) { 16 const lines = elt.querySelector(".src-line-numbers"); 17 let scrollOffset; 18 19 // If the block is greater than the size of the viewer, 20 // then scroll to the top of the block. Otherwise scroll 21 // to the middle of the block. 22 const maxLines = isHidden ? HIDDEN_MAX_LINES : DEFAULT_MAX_LINES; 23 if (loc[1] - loc[0] > maxLines) { 24 const line = Math.max(0, loc[0] - 1); 25 scrollOffset = lines.children[line].offsetTop; 26 } else { 27 const wrapper = elt.querySelector(".code-wrapper"); 28 const halfHeight = wrapper.offsetHeight / 2; 29 const offsetTop = lines.children[loc[0]].offsetTop; 30 const lastLine = lines.children[loc[1]]; 31 const offsetBot = lastLine.offsetTop + lastLine.offsetHeight; 32 const offsetMid = (offsetTop + offsetBot) / 2; 33 scrollOffset = offsetMid - halfHeight; 34 } 35 36 lines.scrollTo(0, scrollOffset); 37 elt.querySelector(".rust").scrollTo(0, scrollOffset); 38 } 39 40 function updateScrapedExample(example, isHidden) { 41 const locs = JSON.parse(example.attributes.getNamedItem("data-locs").textContent); 42 let locIndex = 0; 43 const highlights = Array.prototype.slice.call(example.querySelectorAll(".highlight")); 44 const link = example.querySelector(".scraped-example-title a"); 45 46 if (locs.length > 1) { 47 // Toggle through list of examples in a given file 48 const onChangeLoc = changeIndex => { 49 removeClass(highlights[locIndex], "focus"); 50 changeIndex(); 51 scrollToLoc(example, locs[locIndex][0], isHidden); 52 addClass(highlights[locIndex], "focus"); 53 54 const url = locs[locIndex][1]; 55 const title = locs[locIndex][2]; 56 57 link.href = url; 58 link.innerHTML = title; 59 }; 60 61 example.querySelector(".prev") 62 .addEventListener("click", () => { 63 onChangeLoc(() => { 64 locIndex = (locIndex - 1 + locs.length) % locs.length; 65 }); 66 }); 67 68 example.querySelector(".next") 69 .addEventListener("click", () => { 70 onChangeLoc(() => { 71 locIndex = (locIndex + 1) % locs.length; 72 }); 73 }); 74 } 75 76 const expandButton = example.querySelector(".expand"); 77 if (expandButton) { 78 expandButton.addEventListener("click", () => { 79 if (hasClass(example, "expanded")) { 80 removeClass(example, "expanded"); 81 scrollToLoc(example, locs[0][0], isHidden); 82 } else { 83 addClass(example, "expanded"); 84 } 85 }); 86 } 87 88 // Start with the first example in view 89 scrollToLoc(example, locs[0][0], isHidden); 90 } 91 92 const firstExamples = document.querySelectorAll(".scraped-example-list > .scraped-example"); 93 onEachLazy(firstExamples, el => updateScrapedExample(el, false)); 94 onEachLazy(document.querySelectorAll(".more-examples-toggle"), toggle => { 95 // Allow users to click the left border of the <details> section to close it, 96 // since the section can be large and finding the [+] button is annoying. 97 onEachLazy(toggle.querySelectorAll(".toggle-line, .hide-more"), button => { 98 button.addEventListener("click", () => { 99 toggle.open = false; 100 }); 101 }); 102 103 const moreExamples = toggle.querySelectorAll(".scraped-example"); 104 toggle.querySelector("summary").addEventListener("click", () => { 105 // Wrapping in setTimeout ensures the update happens after the elements are actually 106 // visible. This is necessary since updateScrapedExample calls scrollToLoc which 107 // depends on offsetHeight, a property that requires an element to be visible to 108 // compute correctly. 109 setTimeout(() => { 110 onEachLazy(moreExamples, el => updateScrapedExample(el, true)); 111 }); 112 }, {once: true}); 113 }); 114})(); 115