PageSourceSearch

https://jeapostrophe.github.io/old-blog/manual-racket.js

js jeapostrophe.github.io collected 2026-10-03 09:22:45 UTC 8,947 bytes, 247 lines download raw bytes

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

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.