1/* For the Racket manual style */ 2 3AddOnLoad(function() { 4 /* Look for header elements that have x-source-module and x-part tag. 5 For those elements, add a hidden element that explains how to 6 link to the section, and set the element's onclick() to display 7 the explanation. */ 8 var tag_names = ["h1", "h2", "h3", "h4", "h5"]; 9 for (var j = 0; j < tag_names.length; j++) { 10 elems = document.getElementsByTagName(tag_names[j]); 11 for (var i = 0; i < elems.length; i++) { 12 var elem = elems.item(i); 13 AddPartTitleOnClick(elem); 14 } 15 } 16}) 17 18// for copies that start in Racket code, strip out extra newlines 19AddOnLoad(function() { 20 const codeBlocks = document.getElementsByClassName("SCodeFlow"); 21 for (var i = 0; i < codeBlocks.length; i++) { 22 var codeBlock = codeBlocks[i]; 23 console.log("add"); 24 codeBlock.addEventListener('copy', function(e) { 25 var selection = window.getSelection(); 26 var text = selection.toString(); 27 var codeText = text.replace(/\n\n/g, '\n'); 28 if (text != codeText) { 29 e.preventDefault(); 30 e.clipboardData.setData('text/plain', codeText); 31 } 32 }) 33 } 34}) 35 36// cache of source urls 37var cache = {}; 38 39function ParseSource(source, mod_path, single_collection) { 40 41 var source_url = new URL(source); 42 43 if (source_url.protocol == "github:") { 44 // browser URL parser only works with http(s) URLs 45 source_url = new URL("https" + source.substring(6)); 46 var host = source_url.host; 47 var url_path = source_url.pathname.substring(1).split("/"); 48 if (!(url_path.length >= 2)) return null; 49 var user = url_path.shift(); 50 var repo = url_path.shift(); 51 var branch = url_path.shift(); 52 var source_path = url_path.join("/"); 53 } 54 else if (("https:" == source_url.protocol) || ("git:" == source_url.protocol)) { 55 // browser URL parser only works with http(s) URLs 56 if ("git:" == source_url.protocol) 57 source_url = new URL("https" + source.substring(3)); 58 59 var host = source_url.host; 60 var source_path = source_url.searchParams.get("path"); 61 var branch = (source_url.hash || "#master").substring(1); 62 var url_path = source_url.pathname.substring(1).split("/"); 63 if (url_path.length < 2) throw [source_url.pathname, url_path]; 64 var user = url_path.shift(); 65 var repo = url_path.shift(); 66 var mtch = repo.match(/(.*)\.git$/); 67 if (mtch) repo = mtch[1]; 68 69 } 70 else return null; 71 72 var mod_path_re = /^\(lib "(.+)"\)$/; 73 74 var mod_path_elems = mod_path && mod_path.match(mod_path_re)[1].split("/"); 75 76 if (!user || !repo || !mod_path_elems) 77 return null; 78 if (single_collection) 79 mod_path_elems.shift(); 80 81 var file_path = mod_path_elems.join("/"); 82 83 84 if (source_path) { 85 file_path = source_path + "/" + file_path; 86 } 87 88 return { user: user, 89 repo: repo, 90 file_path: file_path, 91 branch: branch, 92 host: host }; 93} 94 95function AddSourceElement(pkg_url, info) { 96 info.appendChild(document.createTextNode("Document source ")); 97 var url_line = document.createElement("div"); 98 var a = document.createElement("a"); 99 a.href = pkg_url; 100 a.style.whiteSpace = "nowrap"; 101 a.appendChild(document.createTextNode(pkg_url)); 102 addSpan(url_line, "\xA0", "RktRdr"); 103 url_line.appendChild(a); 104 info.appendChild(url_line); 105} 106 107var prefixes = { "github.com": "tree", 108 "gitlab.com": "-/blob" }; 109 110 111function AddSourceUrl(source, mod_path, collection, info) { 112 // multi is encoded as an array, empty as false 113 single_collection = (typeof collection === "string"); 114 115 var parsed = source && mod_path && ParseSource(source, mod_path, single_collection); 116 117 if (!parsed) return; 118 119 prefix = prefixes.hasOwnProperty(parsed.host) && prefixes[parsed.host]; 120 if (!prefix) return; 121 122 var correct_url = "https://" + [parsed.host, parsed.user, parsed.repo, prefix, parsed.branch, parsed.file_path].join("/"); 123 124 if (info) AddSourceElement(correct_url, info); 125} 126 127function addSpan(dest, str, cn) { 128 var s = document.createElement("span"); 129 s.className = cn; 130 s.style.whiteSpace = "nowrap"; 131 s.appendChild(document.createTextNode(str)); 132 dest.appendChild(s); 133} 134 135 136// test cases 137if (false) { 138 console.log(ParseSource("git://gitlab.com/benn/foo?path=xxx", 139 '(lib "asn1/scribblings/asn1.scrbl")', 140 false))
141 console.log(ParseSource("github://github.com/carl-eastlund/mischief/master", 142 '(lib "asn1/scribblings/asn1.scrbl")', 143 false)) 144 console.log(ParseSource("github://github.com/carl-eastlund/mischief/stable/dir", 145 '(lib "asn1/scribblings/asn1.scrbl")', 146 false)) 147 148 console.log(ParseSource("git://github.com/racket/racket/?path=pkgs/racket-doc", 149 '(lib "asn1/scribblings/asn1.scrbl")', 150 false)); 151 152 console.log(ParseSource("git://github.com/rmculpepper/asn1.git?path=asn1-doc", 153 '(lib "asn1/scribblings/asn1.scrbl")', 154 true)); 155 console.log(ParseSource("git://github.com/rmculpepper/asn1", 156 '(lib "asn1/scribblings/asn1.scrbl")', 157 true)); 158 console.log(ParseSource("git://github.com/rmculpepper/asn1", 159 '(lib "asn1/scribblings/asn1.scrbl")', 160 false)); 161} 162 163function AddPartTitleOnClick(elem) { 164 var mod_path = elem.getAttribute("x-source-module"); 165 var tag = elem.getAttribute("x-part-tag"); 166 var source_pkg = elem.getAttribute("x-source-pkg"); 167 168 // create here to share 169 var info = document.createElement("div"); 170 171 172 // tag is not needed, but this way we can add the element in only one place 173 // avoid failing on browser that don't have `fetch` 174 if (mod_path && source_pkg && tag && window.fetch) { 175 176 var cached = cache[mod_path] 177 if (cached) { 178 AddSourceElement(cached[0], mod_path, cached[1], info); 179 } 180 else { 181 fetch("https://pkgs.racket-lang.org/pkg/" + source_pkg + ".json") 182 .then(function (response) { return response.json(); }) 183 .then(function (data) { 184 var vers = data["versions"] || {}; 185 var def = vers["default"] || {}; 186 var source = def["source"] || undefined; 187 var collection = data["collection"]; 188 if (source) { 189 cache[mod_path] = [source, collection]; 190 AddSourceUrl(source, mod_path, collection, info); 191 } 192 }); 193 } 194 } 195 196 if (mod_path && tag) { 197 // Might not be present: 198 var prefixes = elem.getAttribute("x-part-prefixes"); 199 200 info.className = "RPartExplain"; 201 202 /* The "top" tag refers to a whole document: */ 203 var is_top = (tag == "\"top\""); 204 info.appendChild(document.createTextNode("Link to this " 205 + (is_top ? "document" : "section") 206 + " with ")); 207 208 /* Break `secref` into two lines if the module path and tag 209 are long enough: */ 210 var is_long = (is_top ? false : ((mod_path.length 211 + tag.length 212 + (prefixes ? (16 + prefixes.length) : 0)) 213 > 60)); 214 215 var line1 = document.createElement("div"); 216 var line1x = ((is_long && prefixes) ? document.createElement("div") : line1); 217 var line2 = (is_long ? document.createElement("div") : line1); 218 219 /* Construct a `secref` call with suitable syntax coloring: */ 220 addSpan(line1, "\xA0@", "RktRdr"); 221 addSpan(line1, (is_top ? "other-doc" : "secref"), "RktSym"); 222 addSpan(line1, "[", "RktPn"); 223 if (!is_top) 224 addSpan(line1, tag, "RktVal"); 225 if (is_long) { 226 /* indent additional lines: */ 227 if (prefixes) 228 addSpan(line1x, "\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0", "RktPn"); 229 addSpan(line2, "\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0", "RktPn"); 230 } 231 if (prefixes) { 232 addSpan(line1x, " #:tag-prefixes ", "RktPn"); 233 addSpan(line1x, "'", "RktVal"); 234 addSpan(line1x, prefixes, "RktVal"); 235 } 236 if (!is_top)
237 addSpan(line2, " #:doc ", "RktPn"); 238 addSpan(line2, "'", "RktVal"); 239 addSpan(line2, mod_path, "RktVal"); 240 addSpan(line2, "]", "RktPn"); 241 242 info.appendChild(line1); 243 if (is_long) 244 info.appendChild(line1x); 245 if (is_long) 246 info.appendChild(line2); 247 248 info.style.display = "none"; 249 250 /* Add the new element afterthe header: */ 251 var n = elem.nextSibling; 252 if (n) 253 elem.parentNode.insertBefore(info, n); 254 else 255 elem.parentNode.appendChild(info); 256 257 /* Clicking the information button shows the explanation element: */ 258 const heading = elem.querySelector('.heading-source'); 259 if (heading) { 260 heading.onclick = function () { 261 if (info.style.display === "none") 262 info.style.display = "block"; 263 else 264 info.style.display = "none"; 265 } 266 } 267 } 268}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.