Fix relative URLs when serving the specification with a custom baseURL
(#1984)
This commit is contained in:
parent
b1f66d1b71
commit
bf8dee74eb
61 changed files with 101 additions and 92 deletions
168
assets/css/fonts/Inter.css
Normal file
168
assets/css/fonts/Inter.css
Normal file
|
@ -0,0 +1,168 @@
|
|||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: url(../../fonts/Inter-cyrillic-ext-normal-300.woff2);
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: url(../../fonts/Inter-cyrillic-normal-300.woff2);
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: url(../../fonts/Inter-greek-ext-normal-300.woff2);
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: url(../../fonts/Inter-greek-normal-300.woff2);
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: url(../../fonts/Inter-vietnamese-normal-300.woff2);
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: url(../../fonts/Inter-latin-ext-normal-300.woff2);
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: url(../../fonts/Inter-latin-normal-300.woff2);
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url(../../fonts/Inter-cyrillic-ext-normal-400.woff2);
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url(../../fonts/Inter-cyrillic-normal-400.woff2);
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url(../../fonts/Inter-greek-ext-normal-400.woff2);
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url(../../fonts/Inter-greek-normal-400.woff2);
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url(../../fonts/Inter-vietnamese-normal-400.woff2);
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url(../../fonts/Inter-latin-ext-normal-400.woff2);
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url(../../fonts/Inter-latin-normal-400.woff2);
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: url(../../fonts/Inter-cyrillic-ext-normal-700.woff2);
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: url(../../fonts/Inter-cyrillic-normal-700.woff2);
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: url(../../fonts/Inter-greek-ext-normal-700.woff2);
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: url(../../fonts/Inter-greek-normal-700.woff2);
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: url(../../fonts/Inter-vietnamese-normal-700.woff2);
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: url(../../fonts/Inter-latin-ext-normal-700.woff2);
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: url(../../fonts/Inter-latin-normal-700.woff2);
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
30
assets/css/fonts/README.md
Normal file
30
assets/css/fonts/README.md
Normal file
|
@ -0,0 +1,30 @@
|
|||
# Fonts
|
||||
|
||||
## Inter.css
|
||||
|
||||
`Inter.css` is a local copy of
|
||||
https://fonts.googleapis.com/css?family=Inter:300,300i,400,400i,700,700i, modified to pull
|
||||
font files (`.woff2`) from local sources. It was created
|
||||
using `download_google_fonts_css.py`.
|
||||
|
||||
## download_google_fonts_css.py
|
||||
|
||||
`download_google_fonts_css.py` is a script that takes a google fonts CSS URL, downloads
|
||||
the file and linked fonts, then saves the fonts locally along with a modified CSS file to
|
||||
load them. Example call:
|
||||
|
||||
```sh
|
||||
python3 download_google_fonts_css.py \
|
||||
"https://fonts.googleapis.com/css?family=Inter:300,300i,400,400i,700,700i" \
|
||||
../../../static/fonts \
|
||||
../../fonts
|
||||
```
|
||||
|
||||
Which would pop out a `Inter.css` file that should be `@import url("Inter.css")`d
|
||||
somewhere in the site's SCSS (currently in
|
||||
[/assets/scss/_variables_project.scss](/assets/scss/_variables_project.scss)).
|
||||
|
||||
Re-running the script and committing any new files is only necessary when a desired
|
||||
font updates (not very often), or we want to change the font we're using. In that case,
|
||||
remove the existing font files at `/static/fonts/*.woff2` and re-run the script with a
|
||||
different URL.
|
134
assets/css/fonts/download_google_fonts_css.py
Executable file
134
assets/css/fonts/download_google_fonts_css.py
Executable file
|
@ -0,0 +1,134 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# This script takes a google fonts CSS URL, downloads the referenced fonts locally
|
||||
# and spits out a modified CSS file which now points to those local fonts.
|
||||
#
|
||||
# Purely for the purposes of converting a website to use local font files instead of
|
||||
# making requests to Google Fonts.
|
||||
|
||||
import re
|
||||
import requests
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
# Pull the font filename to process and output directory to point at
|
||||
if len(sys.argv) < 4:
|
||||
print(f"""
|
||||
Error: Not enough arguments.
|
||||
|
||||
Usage: {sys.argv[0]} google_fonts_url font_directory css_font_path
|
||||
* google fonts url: A URL leading to a google font css file (i.e https://fonts.googleapis.com/css?family=Inter)
|
||||
* font directory: The location that font files will be downloaded to (i.e ../../fonts)
|
||||
* font path: The directory the resulting CSS will be pointing to, relative to site root (i.e /unstable/css/fonts).
|
||||
This is where the browser will look for fonts.
|
||||
""")
|
||||
sys.exit(1)
|
||||
|
||||
google_fonts_url = sys.argv[1]
|
||||
font_output_dir = sys.argv[2]
|
||||
css_font_path = sys.argv[3]
|
||||
|
||||
# Get font name
|
||||
if google_fonts_url.count(":") > 1:
|
||||
# Account for font weights specified in the URL
|
||||
# (i.e https://fonts.googleapis.com/css?family=Inter:300,300i,400,400i,700,700i)
|
||||
url_match = re.match(r".*family=(.*):", google_fonts_url)
|
||||
else:
|
||||
url_match = re.match(r".*family=(.*)", google_fonts_url)
|
||||
|
||||
if not url_match:
|
||||
print("Unable to extract font name, invalid google fonts URL:", google_fonts_url)
|
||||
print("URL should look something like: https://fonts.googleapis.com/css?family=Inter...")
|
||||
sys.exit(1)
|
||||
|
||||
font_name = url_match.group(1)
|
||||
|
||||
# Ensure font paths end with a trailing slash
|
||||
if not font_output_dir.endswith("/"):
|
||||
font_output_dir += "/"
|
||||
if not css_font_path.endswith("/"):
|
||||
css_font_path += "/"
|
||||
|
||||
# Download the css file and split by newline
|
||||
resp = requests.get(
|
||||
google_fonts_url,
|
||||
# We need to set a believable user-agent, else google fonts won't give us
|
||||
# all of the font weights we requested for some reason
|
||||
headers={
|
||||
"User-Agent": "User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:86.0) Gecko/20100101 Firefox/86.0"
|
||||
}
|
||||
)
|
||||
if resp.status_code != 200:
|
||||
print("Failed to download:", google_fonts_url, resp.status_code)
|
||||
sys.exit(1)
|
||||
|
||||
original_contents = resp.text.split("\n")
|
||||
|
||||
# Download all referenced font files and write out new font file
|
||||
new_css_file_lines = []
|
||||
|
||||
# Store metadata for helping give friendly names to each font file
|
||||
font_lang = None
|
||||
font_family = None
|
||||
font_style = None
|
||||
font_weight = 0
|
||||
for line in original_contents:
|
||||
# Check if this line contains a font URL
|
||||
match = re.match(r".*url\((.*)\) format.*", line)
|
||||
if match:
|
||||
# Download the font file
|
||||
font_url = match.group(1)
|
||||
print("Downloading font:", font_url)
|
||||
|
||||
resp = requests.get(font_url)
|
||||
if resp.status_code == 200:
|
||||
# Save the font file
|
||||
filename = "%s-%s-%s-%d.woff2" % (
|
||||
font_family, font_lang, font_style, font_weight
|
||||
)
|
||||
font_filepath = font_output_dir + filename
|
||||
with open(font_filepath, "wb") as f:
|
||||
print("Writing font file:", font_filepath)
|
||||
f.write(resp.content)
|
||||
|
||||
# Replace google URL with local URL
|
||||
line = re.sub(r"url\(.+\)", f"url({css_font_path + filename})", line)
|
||||
else:
|
||||
print("Warning: failed to download font file:", font_url)
|
||||
|
||||
# Check for font metadata. If there is some, we'll note it down and use it to help
|
||||
# name font files when we write them out.
|
||||
# Makes for nicer font filenames than fvQtMwCp50KnMw2boKod... etc.
|
||||
font_lang_match = re.match(r"^/\* (.+) \*/$", line)
|
||||
if font_lang_match:
|
||||
font_lang = font_lang_match.group(1)
|
||||
font_family_match = re.match(r".*font-family: '(.+)';$", line)
|
||||
if font_family_match:
|
||||
font_family = font_family_match.group(1)
|
||||
font_style_match = re.match(r".*font-style: (.+);$", line)
|
||||
if font_style_match:
|
||||
font_style = font_style_match.group(1)
|
||||
font_weight_match = re.match(r".*font-weight: (.+);$", line)
|
||||
if font_weight_match:
|
||||
font_weight = int(font_weight_match.group(1))
|
||||
|
||||
# Append the potentially modified line to the new css file
|
||||
new_css_file_lines.append(line)
|
||||
|
||||
# Write out the new font css file
|
||||
with open(font_name + ".css", "w") as f:
|
||||
new_css_file_contents = "\n".join(new_css_file_lines)
|
||||
f.write(new_css_file_contents)
|
1
assets/css/fonts/requirements.txt
Normal file
1
assets/css/fonts/requirements.txt
Normal file
|
@ -0,0 +1 @@
|
|||
requests
|
17
assets/diagrams/README.md
Normal file
17
assets/diagrams/README.md
Normal file
|
@ -0,0 +1,17 @@
|
|||
# Spec diagrams
|
||||
|
||||
Non-ascii diagrams for the spec can be placed here for reference in the actual spec.
|
||||
Please include source material so the diagram can be recreated by a future editor.
|
||||
|
||||
https://www.diagrams.net/ is a great ([open source](https://github.com/jgraph/drawio))
|
||||
tool for these sorts of things - include your `.drawio` file next to your diagram.
|
||||
|
||||
Suggested settings for diagrams.net:
|
||||
* Export as PNG.
|
||||
* 100% size.
|
||||
* `20` for a border width.
|
||||
* No transparent background, shadow, or grid.
|
||||
* Include a copy of the diagram.
|
||||
|
||||
To reference a diagram, use the absolute path when compiled. For example,
|
||||
``
|
1
assets/diagrams/membership.drawio
Normal file
1
assets/diagrams/membership.drawio
Normal file
|
@ -0,0 +1 @@
|
|||
<mxfile host="Electron" modified="2022-02-18T05:31:13.369Z" agent="5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/16.5.1 Chrome/96.0.4664.110 Electron/16.0.7 Safari/537.36" etag="FwSKbslSItXwNPoLLuzM" version="16.5.1" type="device"><diagram id="4a_pTli-mcEMNPq0ciXK" name="Page-1">1VvZkto4FP0aqjIPdNnyBo9peklSnZmeoWayvKQECHC3sSghN5CvHxnLeFMbyeCFh1TQ9ZUX6Zyje6/UPWO02j0SuF5+xTPk9YA22/WMux4Auu4M2H+hZR9ZHE2LDAvizrhTYhi7vxE3xm6BO0ObjCPF2KPuOmucYt9HU5qxQULwNus2x172qWu4QAXDeAq9ovWbO6PLyDoATmL/hNzFMn6ybg+jKysYO/Mv2SzhDG9TJuO+Z4wIxjT6tdqNkBcOXjwuUb+Hd64eX4wgn8p0eNas3x+fbr2x/3O79n9ao2BI+zq/zRv0Av7F7A4v2PX5S9N9PBJoxgaGNzGhS7zAPvTuE+stwYE/Q+HjNNZKfJ4wXjOjzowviNI9n2UYUMxMS7ry+NXiJ/HX2+CATFHZd3BoQLJAtMQPRH7ht6QewAfsEeEVomTPHDh++9qNo5tG1IkgD1L3LQsMyPG1OPblt/tICNynHNZsTOkm9bTn0JA8Cgw5TjhRDDs3nWr+7Ef0Brne8evg+XzDRirbIzUEiekAIxVIFRDl+m8uRQVAZeGyXTKf8RoepnnLxEQSGm+IULSTmUymPZkRAxZvbxNi67E8LVOkjv1Ec50ZPeWhskXs8xBkIAMaJr2Q0g+v7vT1utgIJNloqrFRu9GG9qAKGZvEPwCiSZ3AVhQV7Vz6Pex+Y/HWj9SVux2/86Gxjxs+G4CoE7Di9rFb2Ej6HVpxx/oBYyjLt21Vk+9GEVMAjHABVtLLOfbpA1y5Xjg5Izb+Lgrl5E+0rUdMTbN1MR2IeBf4bTMvxTsWTVhy1NPTvIu71UI948JaLU2zsybbKMy1aJo7xRjLygVsoHXGCFeqSHs0d94Dtsde4XbChsFehL+g5+EtG0+gTfbFq2HHXyTwWLpWuPZhHUw8d9oDoxBDdPpHu4shqLQaOpnVUKuNkqYkJWWTmWYoqRc5ye7zTuzfxGy/N3GnwhixcoNy5W5gti3l4MfqeuSjOx2MlXWVeU9Qll2xQX0rtixgZIPlZuTBLMw0z3O7vGYb+SJL62u2MMhtUWSF+aVWiTL1MQYM69NYx7TPU9kYiPnqlGNlbxF9Je8lKgw2INfaiYBRO0aI+QAwDBi1q4kQs0uAbNKWw3ONSZs8oC+9BsiB17TlwKtcJAe553CSvFskL/gPcrxIFckvVs4R7qm0FdQkUXC1IFihcNgAntULgANg6LUItGXWg3Fdzz3HKN8IsrVS//Y2goBwd6MDsUo1cVdM/zOh6SfkvSHqTuF5LKkrVWQkN0GljRVVcBsxaeIoRysR5CbBanRKtIVhiDRQwdUAVV3OHcs6U84bEL5irvvqY8Embqdz3eN610Su+/W7/up/s/99HgXOuK/dYQT+FpwkCHfDhSN5ZamuZHUox4uKZLRkVw3zFPlMfQA6RD4haIAINOJSU/cTSkUpbwM1p0EzMIYdAk3p11740FLTm/CNnmgSsq+48l3qANNLsFrH/pBMz9Vw53wRl6zvXIaOsgnySToOHKuRSF83c6VzKxPpnyzV5PxbTGOHBUzf7ygiDHe98J0LhUwfH16M/Vuh1QSRYiUzQbauKCJS8bnCmYRcecwQ6AcQ6Idd1/aGIaww85gvKjEXR/xwuQ8nbHhLDyW0GTOCKnpzc5lELi2dPvZRbIre2mmoEq142DPM9WzTaUKqBrnExywvuJlmqX+LJ6+LStV+DaMR6BeDgwYiX/XixdDQzCYADYbZKptuqZWQc/4trr3C8yE8m8tLfdvn5c88a301RTt1IT9zh/yiOPuCbNsb//X58fafh/92v54o+fFZcJw0F3VUDTny19rFZaWkR/H0XHUASu9uqAPQzMhbv8VzcayZ/Mlf5J784aRx/z8=</diagram></mxfile>
|
BIN
assets/diagrams/membership.png
Normal file
BIN
assets/diagrams/membership.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
1
assets/diagrams/threaded-dag-threads.drawio
Normal file
1
assets/diagrams/threaded-dag-threads.drawio
Normal file
|
@ -0,0 +1 @@
|
|||
<mxfile host="app.diagrams.net" modified="2022-09-27T03:26:23.216Z" agent="5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" etag="YZcXq9Sm_7Lqw5o2RvSU" version="14.6.7" type="device"><diagram id="_rQ0dgHO1UnHExDn0l7E" name="Page-1">7ZpdU+IwFIZ/DZc6bdNWuJQCujPquOvOrl452TbQaNowIXztr9+EJv0goLCirYwyo81JGpL3PQ8nU2mBIFlcMDiOr2mESMuxokUL9FqO41i+I/7IyDKL2JbvZZERw5GKFYE7/BfpgSo6xRGaVAZySgnH42owpGmKQl6JQcbovDpsSEn1XcdwhIzAXQiJGf2NIx5n0bZzVsQvER7F+p1tv5P1JFAPVjuZxDCi81II9FsgYJTy7CpZBIhI9bQu2X2DLb35whhK+S43/PIf6YMfPSfXNzfx1WP3ez/2ToDaxwySqdrxz5ghGInYudQaJ4jgFKkt8KXWhdFpGiE5td0C3XmMObobw1D2zkUqiFjME6K6hzTlA5hgIrPgEpEZ4jiEsgMTElBC2WpS0O/Jl4jPEJMjyDnBo1T0cTpW09ypJajty4FosVURO9dZZCiiCeJsKYbo9NTWqOT0dHteOO2c+acqY+Oyz201FKr8GuWzFxaIC+XCPo742x3pHrsjAFQdcS3TEdu3Nvhhv5sfruHHNcTpsTuRq7zUHJhO5G59jBO2YcS5If0rYsPJOCsSQ7yQBq2LHAR9byDW2D2Egp2qgsDeoOAGAcG76dc25EKRKHaqSRmP6YimkPSLaLeay8WYKyrTbhV8QpwvVeWGU06rkqMF5vfi2lLXD/JafKRmrd6i1NVb6kYq9nuvJ5CN0l2yWdy2aun7Kiz9FHhOxHZv0Fz8/kETmObGyn2/bKuQiU5ZiF7QUx1tOGQjxF/LWzNNGCKQ41l1HQc33TGg6TYZGsdvGDQueDM0T9NkrMenNEU1cFRC56GM1RaOCPyDSBeGz6PVTkpeDwL5eqlsHRAw8BkAAwZgwZsBOwRIbn5u1afbjlczSs4XSnWh5O6IklMnSuZJu9cElMBaTXLdumuSewwg7Xm2awhI3o4ggTpB8gyQ+o0AyW5eTbINYb5Q+iCU/B1RcutEyXwaN2gCSu76g7K6axLoGEIlpwzBkGOaHgNje577IjiJ8+XLxi3kHLF0FXEstzkYnr2toqmEPLFObfFTSUpbfbjvTKqa/ZZisYliCB0OJ2Jp6ymaL+L/s9b878dFE+g2C6Xv1lwoQXsD3yjC/BjY3rN+fh62Ozuy7dVZYs3KcdkECF3QvNOqZQhz/LQ1BCX9ILrZj1D0KkswfWsETO6HPdcXzeJLFdlBofhuCuj/Aw==</diagram></mxfile>
|
BIN
assets/diagrams/threaded-dag-threads.png
Normal file
BIN
assets/diagrams/threaded-dag-threads.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
1
assets/diagrams/threaded-dag.drawio
Normal file
1
assets/diagrams/threaded-dag.drawio
Normal file
|
@ -0,0 +1 @@
|
|||
<mxfile host="app.diagrams.net" modified="2022-09-27T03:11:43.523Z" agent="5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" etag="L_ujIRop4Jndk67DcTE9" version="14.6.7" type="device"><diagram id="_rQ0dgHO1UnHExDn0l7E" name="Page-1">7VpbU+IwFP41PMqkTS/wKDfdGXXcdWdXn5wsDTTaNkwIAvvrN6UJbYlCF1gbXcYZTU5P0ub0u6S1DdiNFxcMTcJrGuCoYYNg0YC9hm3bwLPFnzSyzCIW8NwsMmYkkLE8cEd+Y5UoozMS4GkpkVMacTIpB4c0SfCQl2KIMTovp41oVD7rBI2xFrgbokiP/iQBD7Noy/bz+CUm41Cd2fLa2ZEYqWS5kmmIAjovhGC/AbuMUp614kUXR2n1VF2ycYM3jq4vjOGEVxnww3ukD17wHF/f3IRXj52v/dA9k7O8oGgmF3wur5YvVQnmIeH4boKGaX8u7nMDdkIeR6JniSaaTrLCj8gCi3N15JSYcbx481qtdQUEdjCNMWdLkaIGQFk0BRtH9uf5PVApYaH8KobkXR+vZ84LIxqyNn9Tp5ZWFhwIoMguZTykY5qgqJ9HO4zOkiAtyapOec4VpRMZfMKcLyXq0YzTcmnxgvB70Qay/ZC2m67s9RaFQ72l6iRivfdqgrRTGJV282Grnho3ogkfoJhEaeA7iQXpbHCD5+L3NxqjZH1j03Vvv62iTHTGhnhLPaUscMTGmO/Cpw4ThiPEyUv5Oo5+022NHB0TyGED08jhn8hxTHLAiuSwayWHr7EjbvKQYRQcjIanWTxR+QlN8IEAadpuASNWNYSApu8WQWLtgEiApuF6AWnnFnGOWbKK2MAR0Qj9wlEHDZ/Hq+V2aUTZqkBw0E1/NoF2iaMXzMkQ1QOvWrUXaujqGqG9vmna2z5p7zHJ4VQkB6xVe52Po71F6QXVALIpvWAHQj6O9FZFV63OroOrZ4L0Qsc06XVP0ntMcrgVyeHUKr3ux5He/ba99ifd9laF13YVOgNNB/pSIitDbjXdOWNoWUiYUJLwaeFst2kgP5WjZpSCp577B2/kW7C1LV80sivYGK0uh45GU1GYTYKsS7A/Z3TK9E0wFMeCTdcwS3FOlnJMznsVOe/Wain6e0ZjLWWv3bz9SXfzVdF16G5+L/dwwYa27XAPG7S25Zfd41jO4GnIHxjhDG3THjXgyReOyVy/InO9Wn1BZ0fcFK4w5IQmRjvDf/+KvSq+Dn2LuJ8ztMrOoH04sOkMbbgt/984g/7fpQsTnME17v2/fZASgJMzlOvZrshcv1ZnAK84Aw4If7ddwrvq/puKXcEQpkIeSDL+vloMyAOi54LXbbCA8wp2UgP2an1abWvQuzRBmD3XNGG2tLKchPkAcqgPQneyo10nOyxdmb+YQA///T6oFN38o9ZsO5h/Gwz7fwA=</diagram></mxfile>
|
BIN
assets/diagrams/threaded-dag.png
Normal file
BIN
assets/diagrams/threaded-dag.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
164
assets/js/toc.js
Normal file
164
assets/js/toc.js
Normal file
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
Copyright 2020, 2021 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Only call the given function once every 250 milliseconds to avoid impacting
|
||||
the performance of the browser.
|
||||
Source: https://remysharp.com/2010/07/21/throttling-function-calls
|
||||
*/
|
||||
function throttle(fn) {
|
||||
const threshold = 250;
|
||||
let last = null;
|
||||
let deferTimer = null;
|
||||
|
||||
return function (...args) {
|
||||
const now = new Date();
|
||||
|
||||
if (last && now < last + threshold) {
|
||||
// Hold on to it.
|
||||
clearTimeout(deferTimer);
|
||||
deferTimer = setTimeout(() => {
|
||||
last = now;
|
||||
fn.apply(this, args);
|
||||
}, threshold);
|
||||
} else {
|
||||
last = now;
|
||||
fn.apply(this, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Get the list of headings that appear in the ToC.
|
||||
This is not as simple as querying all the headings in the content, because
|
||||
some headings are not rendered in the ToC (e.g. in the endpoint definitions).
|
||||
*/
|
||||
function getHeadings() {
|
||||
let headings = [];
|
||||
|
||||
// First get the anchors in the ToC.
|
||||
const toc_anchors = document.querySelectorAll("#toc nav a");
|
||||
|
||||
for (const anchor of toc_anchors) {
|
||||
// Then get the heading from its selector in the anchor's href.
|
||||
const selector = anchor.getAttribute("href");
|
||||
if (!selector) {
|
||||
console.error("Got ToC anchor without href");
|
||||
continue;
|
||||
}
|
||||
|
||||
const heading = document.querySelector(selector);
|
||||
if (!heading) {
|
||||
console.error("Heading not found for selector:", selector);
|
||||
continue;
|
||||
}
|
||||
|
||||
headings.push(heading);
|
||||
}
|
||||
|
||||
return headings;
|
||||
}
|
||||
|
||||
/*
|
||||
Get the heading of the text visible at the top of the viewport.
|
||||
This is the first heading above or at the top of the viewport.
|
||||
*/
|
||||
function getCurrentHeading(headings, headerOffset) {
|
||||
const scrollTop = document.documentElement.scrollTop;
|
||||
let prevHeading = null;
|
||||
let currentHeading = null;
|
||||
let index = 0;
|
||||
|
||||
for (const heading of headings) {
|
||||
// Compute the position compared to the viewport.
|
||||
const rect = heading.getBoundingClientRect();
|
||||
|
||||
if (rect.top >= headerOffset && rect.top <= headerOffset + 30) {
|
||||
// This heading is at the top of the viewport, this is the current heading.
|
||||
currentHeading = heading;
|
||||
break;
|
||||
}
|
||||
if (rect.top >= headerOffset) {
|
||||
// This is in or below the viewport, the current heading should be the
|
||||
// previous one.
|
||||
if (prevHeading) {
|
||||
currentHeading = prevHeading;
|
||||
} else {
|
||||
// The first heading does not have a prevHeading.
|
||||
currentHeading = heading;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
prevHeading = heading;
|
||||
index += 1;
|
||||
}
|
||||
|
||||
return currentHeading;
|
||||
}
|
||||
|
||||
/*
|
||||
Select the ToC entry that points to the given ID.
|
||||
Clear any previously highlighted ToC items, select the new one,
|
||||
and adjust the ToC scroll position.
|
||||
*/
|
||||
function selectTocEntry(id) {
|
||||
// Deselect previously selected entries.
|
||||
const activeEntries = document.querySelectorAll("#toc nav a.active");
|
||||
for (const activeEntry of activeEntries) {
|
||||
activeEntry.classList.remove('active');
|
||||
}
|
||||
|
||||
// Find the new entry and select it.
|
||||
const newEntry = document.querySelector(`#toc nav a[href="#${id}"]`);
|
||||
if (!newEntry) {
|
||||
console.error("ToC entry not found for ID:", id);
|
||||
return;
|
||||
}
|
||||
newEntry.classList.add('active');
|
||||
|
||||
// Don't scroll the sidebar nav if the main content is not scrolled
|
||||
const nav = document.querySelector("#td-section-nav");
|
||||
const content = document.querySelector("html");
|
||||
if (content.scrollTop !== 0) {
|
||||
nav.scrollTop = newEntry.offsetTop - 100;
|
||||
} else {
|
||||
nav.scrollTop = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Track when the view is scrolled, and use this to update the highlight for the
|
||||
corresponding ToC entry.
|
||||
*/
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
// Part of the viewport is below the header so we should take it into account.
|
||||
const headerOffset = document.querySelector("body > header > nav").clientHeight;
|
||||
const headings = getHeadings();
|
||||
|
||||
const onScroll = throttle((_e) => {
|
||||
// Update the ToC.
|
||||
let heading = getCurrentHeading(headings, headerOffset);
|
||||
selectTocEntry(heading.id);
|
||||
});
|
||||
|
||||
// Initialize the state of the ToC.
|
||||
onScroll();
|
||||
|
||||
// Listen to scroll and resizing changes.
|
||||
document.addEventListener('scroll', onScroll, false);
|
||||
document.addEventListener('resize', onScroll, false);
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue