Render a single page of the spec in Hugo
parent
2b32508964
commit
55eae7b70b
@ -0,0 +1,3 @@
|
||||
[submodule "themes/docsy"]
|
||||
path = themes/docsy
|
||||
url = https://github.com/google/docsy.git
|
@ -0,0 +1,5 @@
|
||||
<svg width="75" height="32" xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="#000" fill-rule="nonzero">
|
||||
<path d="M.936.732V31.25H3.13v.732H.095V0h3.034v.732zM9.386 10.407v1.544h.044a4.461 4.461 0 0 1 1.487-1.368c.58-.323 1.245-.485 1.993-.485.72 0 1.377.14 1.972.42.595.279 1.047.771 1.355 1.477.338-.5.796-.941 1.377-1.323.58-.383 1.266-.574 2.06-.574.602 0 1.16.074 1.674.22.514.148.954.383 1.322.707.366.323.653.746.859 1.268.205.522.308 1.15.308 1.887v7.633H20.71v-6.464c0-.383-.015-.743-.044-1.082a2.305 2.305 0 0 0-.242-.882 1.473 1.473 0 0 0-.584-.596c-.257-.146-.606-.22-1.047-.22-.44 0-.796.085-1.068.253-.272.17-.485.39-.639.662a2.654 2.654 0 0 0-.308.927 7.074 7.074 0 0 0-.078 1.048v6.354h-3.128v-6.398c0-.338-.007-.673-.021-1.004a2.825 2.825 0 0 0-.188-.916 1.411 1.411 0 0 0-.55-.673c-.258-.168-.636-.253-1.135-.253a2.33 2.33 0 0 0-.584.1 1.94 1.94 0 0 0-.705.374c-.228.184-.422.449-.584.794-.161.346-.242.798-.242 1.357v6.619H6.434V10.407h2.952zM25.842 12.084a3.751 3.751 0 0 1 1.233-1.17 5.37 5.37 0 0 1 1.685-.629 9.579 9.579 0 0 1 1.884-.187c.573 0 1.153.04 1.74.121.588.081 1.124.24 1.609.475.484.235.88.562 1.19.981.308.42.462.975.462 1.666v5.934c0 .516.03 1.008.088 1.478.058.471.161.824.308 1.06H32.87a4.435 4.435 0 0 1-.22-1.104c-.5.515-1.087.876-1.762 1.081a7.084 7.084 0 0 1-2.071.31c-.544 0-1.05-.067-1.52-.2a3.472 3.472 0 0 1-1.234-.617 2.87 2.87 0 0 1-.826-1.059c-.199-.426-.298-.934-.298-1.522 0-.647.114-1.18.342-1.6.227-.419.52-.753.881-1.004.36-.25.771-.437 1.234-.562.462-.125.929-.224 1.399-.298.47-.073.932-.132 1.387-.176.456-.044.86-.11 1.212-.199.353-.088.631-.217.837-.386.206-.169.301-.415.287-.74 0-.337-.055-.606-.166-.804a1.217 1.217 0 0 0-.44-.464 1.737 1.737 0 0 0-.639-.22 5.292 5.292 0 0 0-.782-.055c-.617 0-1.101.132-1.454.397-.352.264-.558.706-.617 1.323h-3.128c.044-.735.227-1.345.55-1.83zm6.179 4.423a5.095 5.095 0 0 1-.639.165 9.68 9.68 0 0 1-.716.11c-.25.03-.5.067-.749.11a5.616 5.616 0 0 0-.694.177 2.057 2.057 0 0 0-.594.298c-.17.125-.305.284-.408.474-.103.192-.154.434-.154.728 0 .28.051.515.154.706.103.192.242.342.419.453.176.11.381.187.617.231.234.044.477.066.726.066.617 0 1.094-.102 1.432-.309.338-.205.587-.452.75-.739.16-.286.26-.576.297-.87.036-.295.055-.53.055-.707v-1.17a1.4 1.4 0 0 1-.496.277zM43.884 10.407v2.096h-2.291v5.647c0 .53.088.883.264 1.059.176.177.529.265 1.057.265.177 0 .345-.007.507-.022.161-.015.316-.037.463-.066v2.426a7.49 7.49 0 0 1-.882.089 21.67 21.67 0 0 1-.947.022c-.484 0-.944-.034-1.377-.1a3.233 3.233 0 0 1-1.145-.386 2.04 2.04 0 0 1-.782-.816c-.191-.353-.287-.816-.287-1.39v-6.728H36.57v-2.096h1.894v-3.42h3.129v3.42h2.29zM48.355 10.407v2.118h.044a3.907 3.907 0 0 1 1.454-1.754 4.213 4.213 0 0 1 1.036-.497 3.734 3.734 0 0 1 1.145-.176c.206 0 .433.037.683.11v2.912a5.862 5.862 0 0 0-.528-.077 5.566 5.566 0 0 0-.595-.033c-.573 0-1.058.096-1.454.287a2.52 2.52 0 0 0-.958.783 3.143 3.143 0 0 0-.518 1.158 6.32 6.32 0 0 0-.154 1.434v5.14h-3.128V10.407h2.973zM54.039 8.642V6.06h3.128v2.582H54.04zm3.128 1.765v11.405H54.04V10.407h3.128zM58.797 10.407h3.569l2.005 2.978 1.982-2.978h3.459l-3.745 5.339 4.208 6.067h-3.57l-2.378-3.596-2.38 3.596h-3.502l4.097-6.001zM74.094 31.25V.732H71.9V0h3.035v31.982H71.9v-.732z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.1 KiB |
@ -0,0 +1,21 @@
|
||||
$primary: #FFF;
|
||||
$secondary: #0098D4;
|
||||
$dark: #333;
|
||||
$gray-100: #FBFBFB;
|
||||
|
||||
$secondary-background: #E5F5FB;
|
||||
$secondary-lighter-background: #F4FaFC;
|
||||
$secondary-lightest-background: #FBFDFD;
|
||||
|
||||
|
||||
$warning: #FF6666;
|
||||
$note: $secondary;
|
||||
|
||||
$note-background: $secondary-background;
|
||||
$warning-background: #FFE0E0;
|
||||
|
||||
$table-row-alternate: $secondary-lightest-background;
|
||||
$table-row-default: $secondary-lighter-background;
|
||||
|
||||
$google_font_name: "Inter";
|
||||
$google_font_family: "Inter:300,300i,400,400i,700,700i";
|
@ -0,0 +1,234 @@
|
||||
/*
|
||||
Custom SCSS for the Matrix spec
|
||||
*/
|
||||
|
||||
@import "variables_project";
|
||||
@import "variables";
|
||||
|
||||
/* Overrides for the navbar */
|
||||
.td-navbar {
|
||||
box-shadow: 0px 0px 8px rgba(179, 179, 179, 0.25);
|
||||
min-height: 5rem;
|
||||
|
||||
.navbar-brand {
|
||||
font-size: 1.1rem;
|
||||
|
||||
.navbar-version {
|
||||
color: $secondary;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
a {
|
||||
color: $black;
|
||||
}
|
||||
}
|
||||
|
||||
/* Styles for the sidebar nav */
|
||||
.td-sidebar-nav {
|
||||
scroll-behavior: smooth;
|
||||
|
||||
/* This overrides calc(100vh - 10rem);, which gives us a blank space at the bottom of the sidebar */
|
||||
max-height: calc(100vh - 6rem);
|
||||
|
||||
&>.td-sidebar-nav__section {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
&>.td-sidebar-nav__section > li > a.td-sidebar-link {
|
||||
font-weight: $font-weight-bold;
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
/* This is to make the width of the items that have sub-items (like room versions)
|
||||
the same as the width of items that don't (like changelog) */
|
||||
.pr-md-3, .px-md-3 {
|
||||
padding-right: 0 !important;
|
||||
}
|
||||
|
||||
a.indent-1 {
|
||||
padding-left: 1rem !important;
|
||||
}
|
||||
|
||||
a.indent-2 {
|
||||
padding-left: 2rem;
|
||||
}
|
||||
|
||||
a, a.td-sidebar-link {
|
||||
color: $gray-800;
|
||||
font-weight: $font-weight-normal;
|
||||
padding-top: .2rem;
|
||||
padding-bottom: .2rem;
|
||||
|
||||
display: block;
|
||||
transition: all 100ms ease-in-out;
|
||||
|
||||
&:hover {
|
||||
background-color: $secondary-lighter-background;
|
||||
color: $gray-800;
|
||||
}
|
||||
|
||||
&.active, &active:hover {
|
||||
background-color: $secondary-background;
|
||||
font-weight: $font-weight-normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Customise footer */
|
||||
footer {
|
||||
box-shadow: 0px 0px 8px rgba(179, 179, 179, 0.25);
|
||||
}
|
||||
|
||||
/* Auto numbering for headings */
|
||||
.td-content {
|
||||
|
||||
counter-reset: h2;
|
||||
|
||||
&> h2 {
|
||||
counter-reset: h3
|
||||
}
|
||||
|
||||
&> h3 {
|
||||
counter-reset: h4
|
||||
}
|
||||
|
||||
&> h4 {
|
||||
counter-reset: h5
|
||||
}
|
||||
|
||||
&> h5 {
|
||||
counter-reset: h6
|
||||
}
|
||||
|
||||
&> h2:not(.no-numbers):before {
|
||||
display: inline; visibility: visible; counter-increment: h2; content: counter(h2) ". "
|
||||
}
|
||||
|
||||
&> h3:not(.no-numbers):before {
|
||||
display: inline; visibility: visible; counter-increment: h3; content: counter(h2) "." counter(h3) ". "
|
||||
}
|
||||
|
||||
&> h4:not(.no-numbers):before {
|
||||
display: inline; visibility: visible; counter-increment: h4; content: counter(h2) "." counter(h3) "." counter(h4) ". "
|
||||
}
|
||||
|
||||
&> h5:not(.no-numbers):before {
|
||||
display: inline; visibility: visible; counter-increment: h5; content: counter(h2) "." counter(h3) "." counter(h4) "." counter(h5) ". "
|
||||
}
|
||||
|
||||
&> h6:not(.no-numbers):before {
|
||||
display: inline; visibility: visible; counter-increment: h6; content: counter(h2) "." counter(h3) "." counter(h4) "." counter(h5) "." counter(h6) ". "
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Adjust heading anchors for site header */
|
||||
.td-content {
|
||||
&> h2,
|
||||
&> h3,
|
||||
&> h4,
|
||||
&> h5,
|
||||
&> h6,
|
||||
.rendered-data h1 {
|
||||
scroll-margin-top: 5rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Styles for the table of contents */
|
||||
#toc {
|
||||
padding-top: .5rem;
|
||||
padding-left: 1.5rem;
|
||||
|
||||
ol {
|
||||
padding-left: 1rem;
|
||||
counter-reset: section;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
#TableOfContents {
|
||||
&>ol>li {
|
||||
margin-bottom: .5rem;
|
||||
|
||||
&>a {
|
||||
font-weight: $font-weight-bold;
|
||||
}
|
||||
}
|
||||
|
||||
ol {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
&>ol>li>a {
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
&>ol>li>ol>li>a {
|
||||
padding-left: 2rem;
|
||||
}
|
||||
|
||||
&>ol>li>ol>li>ol>li>a {
|
||||
padding-left: 3rem;
|
||||
}
|
||||
|
||||
&>ol>li>ol>li>ol>li>ol>li>a {
|
||||
padding-left: 4rem;
|
||||
}
|
||||
|
||||
&>ol>li>ol>li>ol>li>ol>li>ol>li>a {
|
||||
padding-left: 5rem;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
li a:before {
|
||||
counter-increment: section;
|
||||
content: counters(section, ".") " ";
|
||||
}
|
||||
|
||||
#toc-title {
|
||||
font-weight: $font-weight-bold;
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Miscellaneous custom bits */
|
||||
|
||||
/* Update link colours for MAtrix style */
|
||||
a, a:hover {
|
||||
color: $secondary;
|
||||
}
|
||||
|
||||
/* This is needed to stop the bottom of the Matrix icon from getting snipped off. */
|
||||
.td-navbar .navbar-brand svg {
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
/* Give code samples and pre elements full-width */
|
||||
.td-content > .highlight, .td-content > pre {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/*
|
||||
Make padding symmetrical (this selector is used to apply padding-left: 3rem)
|
||||
*/
|
||||
.pl-md-5, .px-md-5 {
|
||||
padding-right: 3rem;
|
||||
}
|
||||
|
||||
/* Adjust default styles for info banner */
|
||||
.pageinfo-primary {
|
||||
max-width: 80%;
|
||||
margin-left: 0;
|
||||
border: 0;
|
||||
border-left: solid 5px $secondary;
|
||||
background-color: $gray-100;
|
||||
}
|
||||
|
||||
.pageinfo-unstable {
|
||||
background-image: url('/icons/unstable.png');
|
||||
background-position: left 1rem center;
|
||||
background-repeat: no-repeat;
|
||||
padding-left: 100px;
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
baseURL = "/"
|
||||
title = "Matrix Specification"
|
||||
|
||||
enableRobotsTXT = true
|
||||
|
||||
# Hugo allows theme composition (and inheritance). The precedence is from left to right.
|
||||
theme = ["docsy"]
|
||||
|
||||
disableKinds = ["taxonomy", "taxonomyTerm"]
|
||||
|
||||
[languages]
|
||||
[languages.en]
|
||||
title = "Matrix Specification"
|
||||
description = "Home of the Matrix specification for decentralised communication"
|
||||
languageName ="English"
|
||||
# Weight used for sorting.
|
||||
weight = 1
|
||||
|
||||
[markup]
|
||||
[markup.goldmark]
|
||||
[markup.goldmark.renderer]
|
||||
# Enables us to render raw HTML
|
||||
unsafe = true
|
||||
[markup.highlight]
|
||||
# See a complete list of available styles at https://xyproto.github.io/splash/docs/all.html
|
||||
style = "tango"
|
||||
|
||||
# Everything below this are Site Params
|
||||
|
||||
[params]
|
||||
copyright = "The Matrix.org Foundation CIC"
|
||||
privacy_policy = "https://matrix.org/legal/privacy-notice"
|
||||
|
||||
[params.version]
|
||||
# must be one of "unstable", "current", "historical"
|
||||
# this is used to decide whether to show a banner pointing to the current release
|
||||
status = "unstable"
|
||||
current_version_url = "https://spec.matrix.org/latest"
|
||||
|
||||
# User interface configuration
|
||||
[params.ui]
|
||||
# Set to true to disable the About link in the site footer
|
||||
footer_about_disable = false
|
||||
# Collapse HTTP API and event <details> elements
|
||||
rendered_data_collapsed = false
|
||||
|
||||
[params.links]
|
||||
# End user relevant links. These will show up on left side of footer and in the community page if you have one.
|
||||
# [[params.links.user]]
|
||||
# name = "User mailing list"
|
||||
# url = "https://example.org/mail"
|
||||
# icon = "fa fa-envelope"
|
||||
# desc = "Discussion and help from your fellow users"
|
||||
# Developer relevant links. These will show up on right side of footer and in the community page if you have one.
|
||||
[[params.links.developer]]
|
||||
name = "GitHub"
|
||||
url = "https://github.com/matrix-org"
|
||||
icon = "fab fa-github"
|
||||
desc = "Matrix on GitHub"
|
||||
[[params.links.developer]]
|
||||
name = "GitLab"
|
||||
url = "https://gitlab.matrix.org/matrix-org"
|
||||
icon = "fab fa-gitlab"
|
||||
desc = "Matrix on GitLab"
|
||||
[[params.links.developer]]
|
||||
name = "YouTube"
|
||||
url = "https://www.youtube.com/channel/UCVFkW-chclhuyYRbmmfwt6w"
|
||||
icon = "fab fa-youtube"
|
||||
desc = "Matrix YouTube channel"
|
||||
[[params.links.developer]]
|
||||
name = "Twitter"
|
||||
url = "https://twitter.com/matrixdotorg"
|
||||
icon = "fab fa-twitter"
|
||||
desc = "Matrix on Twitter"
|
@ -0,0 +1,553 @@
|
||||
---
|
||||
title: "Matrix Specification"
|
||||
type: docs
|
||||
weight: 10
|
||||
---
|
||||
|
||||
Matrix defines a set of open APIs for decentralised communication,
|
||||
suitable for securely publishing, persisting and subscribing to data
|
||||
over a global open federation of servers with no single point of
|
||||
control. Uses include Instant Messaging (IM), Voice over IP (VoIP)
|
||||
signalling, Internet of Things (IoT) communication, and bridging
|
||||
together existing communication silos - providing the basis of a new
|
||||
open real-time communication ecosystem.
|
||||
|
||||
To propose a change to the Matrix Spec, see the explanations at
|
||||
[Proposals for Spec Changes to Matrix](proposals).
|
||||
|
||||
## Matrix APIs
|
||||
|
||||
The specification consists of the following parts:
|
||||
|
||||
{{apis}}
|
||||
|
||||
Additionally, this introduction page contains the key baseline
|
||||
information required to understand the specific APIs, including the
|
||||
sections on [room versions](#room-versions) and [overall
|
||||
architecture](#architecture).
|
||||
|
||||
The [Appendices](appendices.html) contain supplemental information not
|
||||
specific to one of the above APIs.
|
||||
|
||||
The [Matrix Client-Server API Swagger
|
||||
Viewer](https://matrix.org/docs/api/client-server/) is useful for
|
||||
browsing the Client-Server API.
|
||||
|
||||
### Matrix versions
|
||||
|
||||
Note
|
||||
|
||||
As of June 10th 2019, the Matrix specification is considered out of beta
|
||||
-indicating that all currently released APIs are considered stable and
|
||||
secure to the best of our knowledge, and the spec should contain the
|
||||
complete information necessary to develop production-grade
|
||||
implementations of Matrix without the need for external reference.
|
||||
|
||||
Matrix 1.0 (released June 10th, 2019) consists of the following minimum
|
||||
API versions:
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr class="header">
|
||||
<th>API/Specification</th>
|
||||
<th>Version</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="odd">
|
||||
<td>Client-Server API</td>
|
||||
<td>r0.5.0</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td>Server-Server API</td>
|
||||
<td>r0.1.2</td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<td>Application Service API</td>
|
||||
<td>r0.1.1</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td>Identity Service API</td>
|
||||
<td>r0.1.1</td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<td>Push Gateway API</td>
|
||||
<td>r0.1.0</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td>Room Version</td>
|
||||
<td>v5</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
## Introduction to the Matrix APIs
|
||||
|
||||
Matrix is a set of open APIs for open-federated Instant Messaging (IM),
|
||||
Voice over IP (VoIP) and Internet of Things (IoT) communication,
|
||||
designed to create and support a new global real-time communication
|
||||
ecosystem. The intention is to provide an open decentralised pubsub
|
||||
layer for the internet for securely persisting and
|
||||
publishing/subscribing JSON objects. This specification is the ongoing
|
||||
result of standardising the APIs used by the various components of the
|
||||
Matrix ecosystem to communicate with one another.
|
||||
|
||||
The principles that Matrix attempts to follow are:
|
||||
|
||||
- Pragmatic Web-friendly APIs (i.e. JSON over REST)
|
||||
- Keep It Simple & Stupid
|
||||
- provide a simple architecture with minimal third-party
|
||||
dependencies.
|
||||
- Fully open:
|
||||
- Fully open federation - anyone should be able to participate in
|
||||
the global Matrix network
|
||||
- Fully open standard - publicly documented standard with no IP or
|
||||
patent licensing encumbrances
|
||||
- Fully open source reference implementation - liberally-licensed
|
||||
example implementations with no IP or patent licensing
|
||||
encumbrances
|
||||
- Empowering the end-user
|
||||
- The user should be able to choose the server and clients they
|
||||
use
|
||||
- The user should be able to control how private their
|
||||
communication is
|
||||
- The user should know precisely where their data is stored
|
||||
- Fully decentralised - no single points of control over conversations
|
||||
or the network as a whole
|
||||
- Learning from history to avoid repeating it
|
||||
- Trying to take the best aspects of XMPP, SIP, IRC, SMTP, IMAP
|
||||
and NNTP whilst trying to avoid their failings
|
||||
|
||||
The functionality that Matrix provides includes:
|
||||
|
||||
- Creation and management of fully distributed chat rooms with no
|
||||
single points of control or failure
|
||||
- Eventually-consistent cryptographically secure synchronisation of
|
||||
room state across a global open network of federated servers and
|
||||
services
|
||||
- Sending and receiving extensible messages in a room with (optional)
|
||||
end-to-end encryption
|
||||
- Extensible user management (inviting, joining, leaving, kicking,
|
||||
banning) mediated by a power-level based user privilege system.
|
||||
- Extensible room state management (room naming, aliasing, topics,
|
||||
bans)
|
||||
- Extensible user profile management (avatars, display names, etc)
|
||||
- Managing user accounts (registration, login, logout)
|
||||
- Use of 3rd Party IDs (3PIDs) such as email addresses, phone numbers,
|
||||
Facebook accounts to authenticate, identify and discover users on
|
||||
Matrix.
|
||||
- Trusted federation of identity servers for:
|
||||
- Publishing user public keys for PKI
|
||||
- Mapping of 3PIDs to Matrix IDs
|
||||
|
||||
The end goal of Matrix is to be a ubiquitous messaging layer for
|
||||
synchronising arbitrary data between sets of people, devices and
|
||||
services - be that for instant messages, VoIP call setups, or any other
|
||||
objects that need to be reliably and persistently pushed from A to B in
|
||||
an interoperable and federated manner.
|
||||
|
||||
### Spec Change Proposals
|
||||
|
||||
To propose a change to the Matrix Spec, see the explanations at
|
||||
[Proposals for Spec Changes to Matrix](proposals).
|
||||
|
||||
## Architecture
|
||||
|
||||
Matrix defines APIs for synchronising extensible JSON objects known as
|
||||
"events" between compatible clients, servers and services. Clients are
|
||||
typically messaging/VoIP applications or IoT devices/hubs and
|
||||
communicate by synchronising communication history with their
|
||||
"homeserver" using the "Client-Server API". Each homeserver stores the
|
||||
communication history and account information for all of its clients,
|
||||
and shares data with the wider Matrix ecosystem by synchronising
|
||||
communication history with other homeservers and their clients.
|
||||
|
||||
Clients typically communicate with each other by emitting events in the
|
||||
context of a virtual "room". Room data is replicated across *all of the
|
||||
homeservers* whose users are participating in a given room. As such, *no
|
||||
single homeserver has control or ownership over a given room*.
|
||||
Homeservers model communication history as a partially ordered graph of
|
||||
events known as the room's "event graph", which is synchronised with
|
||||
eventual consistency between the participating servers using the
|
||||
"Server-Server API". This process of synchronising shared conversation
|
||||
history between homeservers run by different parties is called
|
||||
"Federation". Matrix optimises for the Availability and Partitioned
|
||||
properties of CAP theorem at the expense of Consistency.
|
||||
|
||||
For example, for client A to send a message to client B, client A
|
||||
performs an HTTP PUT of the required JSON event on its homeserver (HS)
|
||||
using the client-server API. A's HS appends this event to its copy of
|
||||
the room's event graph, signing the message in the context of the graph
|
||||
for integrity. A's HS then replicates the message to B's HS by
|
||||
performing an HTTP PUT using the server-server API. B's HS authenticates
|
||||
the request, validates the event's signature, authorises the event's
|
||||
contents and then adds it to its copy of the room's event graph. Client
|
||||
B then receives the message from his homeserver via a long-lived GET
|
||||
request.
|
||||
|
||||
How data flows between clients
|
||||
==============================
|
||||
|
||||
{ Matrix client A } { Matrix client B }
|
||||
^ | ^ |
|
||||
| events | Client-Server API | events |
|
||||
| V | V
|
||||
+------------------+ +------------------+
|
||||
| |---------( HTTPS )--------->| |
|
||||
| homeserver | | homeserver |
|
||||
| |<--------( HTTPS )----------| |
|
||||
+------------------+ Server-Server API +------------------+
|
||||
History Synchronisation
|
||||
(Federation)
|
||||
|
||||
### Users
|
||||
|
||||
Each client is associated with a user account, which is identified in
|
||||
Matrix using a unique "user ID". This ID is namespaced to the homeserver
|
||||
which allocated the account and has the form:
|
||||
|
||||
@localpart:domain
|
||||
|
||||
See ['Identifier Grammar' in the
|
||||
appendices](appendices.html#identifier-grammar) for full details of the
|
||||
structure of user IDs.
|
||||
|
||||
### Devices
|
||||
|
||||
The Matrix specification has a particular meaning for the term "device".
|
||||
As a user, I might have several devices: a desktop client, some web
|
||||
browsers, an Android device, an iPhone, etc. They broadly relate to a
|
||||
real device in the physical world, but you might have several browsers
|
||||
on a physical device, or several Matrix client applications on a mobile
|
||||
device, each of which would be its own device.
|
||||
|
||||
Devices are used primarily to manage the keys used for end-to-end
|
||||
encryption (each device gets its own copy of the decryption keys), but
|
||||
they also help users manage their access - for instance, by revoking
|
||||
access to particular devices.
|
||||
|
||||
When a user first uses a client, it registers itself as a new device.
|
||||
The longevity of devices might depend on the type of client. A web
|
||||
client will probably drop all of its state on logout, and create a new
|
||||
device every time you log in, to ensure that cryptography keys are not
|
||||
leaked to a new user. In a mobile client, it might be acceptable to
|
||||
reuse the device if a login session expires, provided the user is the
|
||||
same.
|
||||
|
||||
Devices are identified by a `device_id`, which is unique within the
|
||||
scope of a given user.
|
||||
|
||||
A user may assign a human-readable display name to a device, to help
|
||||
them manage their devices.
|
||||
|
||||
### Events
|
||||
|
||||
All data exchanged over Matrix is expressed as an "event". Typically
|
||||
each client action (e.g. sending a message) correlates with exactly one
|
||||
event. Each event has a `type` which is used to differentiate different
|
||||
kinds of data. `type` values MUST be uniquely globally namespaced
|
||||
following Java's [package naming
|
||||
conventions](https://en.wikipedia.org/wiki/Java_package#Package_naming_conventions),
|
||||
e.g. `com.example.myapp.event`. The special top-level namespace `m.` is
|
||||
reserved for events defined in the Matrix specification - for instance
|
||||
`m.room.message` is the event type for instant messages. Events are
|
||||
usually sent in the context of a "Room".
|
||||
|
||||
### Event Graphs
|
||||
|
||||
Events exchanged in the context of a room are stored in a directed
|
||||
acyclic graph (DAG) called an "event graph". The partial ordering of
|
||||
this graph gives the chronological ordering of events within the room.
|
||||
Each event in the graph has a list of zero or more "parent" events,
|
||||
which refer to any preceding events which have no chronological
|
||||
successor from the perspective of the homeserver which created the
|
||||
event.
|
||||
|
||||
Typically an event has a single parent: the most recent message in the
|
||||
room at the point it was sent. However, homeservers may legitimately
|
||||
race with each other when sending messages, resulting in a single event
|
||||
having multiple successors. The next event added to the graph thus will
|
||||
have multiple parents. Every event graph has a single root event with no
|
||||
parent.
|
||||
|
||||
To order and ease chronological comparison between the events within the
|
||||
graph, homeservers maintain a `depth` metadata field on each event. An
|
||||
event's `depth` is a positive integer that is strictly greater than the
|
||||
depths of any of its parents. The root event should have a depth of 1.
|
||||
Thus if one event is before another, then it must have a strictly
|
||||
smaller depth.
|
||||
|
||||
### Room structure
|
||||
|
||||
A room is a conceptual place where users can send and receive events.
|
||||
Events are sent to a room, and all participants in that room with
|
||||
sufficient access will receive the event. Rooms are uniquely identified
|
||||
internally via "Room IDs", which have the form:
|
||||
|
||||
!opaque_id:domain
|
||||
|
||||
There is exactly one room ID for each room. Whilst the room ID does
|
||||
contain a domain, it is simply for globally namespacing room IDs. The
|
||||
room does NOT reside on the domain specified.
|
||||
|
||||
See ['Identifier Grammar' in the
|
||||
appendices](appendices.html#identifier-grammar) for full details of the
|
||||
structure of a room ID.
|
||||
|
||||
The following conceptual diagram shows an `m.room.message` event being
|
||||
sent to the room `!qporfwt:matrix.org`:
|
||||
|
||||
{ @alice:matrix.org } { @bob:example.org }
|
||||
| ^
|
||||
| |
|
||||
[HTTP POST] [HTTP GET]
|
||||
Room ID: !qporfwt:matrix.org Room ID: !qporfwt:matrix.org
|
||||
Event type: m.room.message Event type: m.room.message
|
||||
Content: { JSON object } Content: { JSON object }
|
||||
| |
|
||||
V |
|
||||
+------------------+ +------------------+
|
||||
| homeserver | | homeserver |
|
||||
| matrix.org | | example.org |
|
||||
+------------------+ +------------------+
|
||||
| ^
|
||||
| [HTTP PUT] |
|
||||
| Room ID: !qporfwt:matrix.org |
|
||||
| Event type: m.room.message |
|
||||
| Content: { JSON object } |
|
||||
`-------> Pointer to the preceding message ------`
|
||||
PKI signature from matrix.org
|
||||
Transaction-layer metadata
|
||||
PKI Authorization header
|
||||
|
||||
....................................
|
||||
| Shared Data |
|
||||
| State: |
|
||||
| Room ID: !qporfwt:matrix.org |
|
||||
| Servers: matrix.org, example.org |
|
||||
| Members: |
|
||||
| - @alice:matrix.org |
|
||||
| - @bob:example.org |
|
||||
| Messages: |
|
||||
| - @alice:matrix.org |
|
||||
| Content: { JSON object } |
|
||||
|....................................|
|
||||
|
||||
Federation maintains *shared data structures* per-room between multiple
|
||||
homeservers. The data is split into `message events` and `state events`.
|
||||
|
||||
Message events:
|
||||
These describe transient 'once-off' activity in a room such as an
|
||||
instant messages, VoIP call setups, file transfers, etc. They generally
|
||||
describe communication activity.
|
||||
|
||||
State events:
|
||||
These describe updates to a given piece of persistent information
|
||||
('state') related to a room, such as the room's name, topic, membership,
|
||||
participating servers, etc. State is modelled as a lookup table of
|
||||
key/value pairs per room, with each key being a tuple of `state_key` and
|
||||
`event type`. Each state event updates the value of a given key.
|
||||
|
||||
The state of the room at a given point is calculated by considering all
|
||||
events preceding and including a given event in the graph. Where events
|
||||
describe the same state, a merge conflict algorithm is applied. The
|
||||
state resolution algorithm is transitive and does not depend on server
|
||||
state, as it must consistently select the same event irrespective of the
|
||||
server or the order the events were received in. Events are signed by
|
||||
the originating server (the signature includes the parent relations,
|
||||
type, depth and payload hash) and are pushed over federation to the
|
||||
participating servers in a room, currently using full mesh topology.
|
||||
Servers may also request backfill of events over federation from the
|
||||
other servers participating in a room.
|
||||
|
||||
Note
|
||||
|
||||
Events are not limited to the types defined in this specification. New
|
||||
or custom event types can be created on a whim using the Java package
|
||||
naming convention. For example, a `com.example.game.score` event can be
|
||||
sent by clients and other clients would receive it through Matrix,
|
||||
assuming the client has access to the `com.example` namespace.
|
||||
|
||||
#### Room Aliases
|
||||
|
||||
Each room can also have multiple "Room Aliases", which look like:
|
||||
|
||||
#room_alias:domain
|
||||
|
||||
See ['Identifier Grammar' in the
|
||||
appendices](appendices.html#identifier-grammar) for full details of the
|
||||
structure of a room alias.
|
||||
|
||||
A room alias "points" to a room ID and is the human-readable label by
|
||||
which rooms are publicised and discovered. The room ID the alias is
|
||||
pointing to can be obtained by visiting the domain specified. Note that
|
||||
the mapping from a room alias to a room ID is not fixed, and may change
|
||||
over time to point to a different room ID. For this reason, Clients
|
||||
SHOULD resolve the room alias to a room ID once and then use that ID on
|
||||
subsequent requests.
|
||||
|
||||
When resolving a room alias the server will also respond with a list of
|
||||
servers that are in the room that can be used to join via.
|
||||
|
||||
HTTP GET
|
||||
#matrix:example.org !aaabaa:matrix.org
|
||||
| ^
|
||||
| |
|
||||
_______V____________________|____
|
||||
| example.org |
|
||||
| Mappings: |
|
||||
| #matrix >> !aaabaa:matrix.org |
|
||||
| #golf >> !wfeiofh:sport.com |
|
||||
| #bike >> !4rguxf:matrix.org |
|
||||
|________________________________|
|
||||
|
||||
### Identity
|
||||
|
||||
Users in Matrix are identified via their Matrix user ID. However,
|
||||
existing 3rd party ID namespaces can also be used in order to identify
|
||||
Matrix users. A Matrix "Identity" describes both the user ID and any
|
||||
other existing IDs from third party namespaces *linked* to their
|
||||
account. Matrix users can *link* third-party IDs (3PIDs) such as email
|
||||
addresses, social network accounts and phone numbers to their user ID.
|
||||
Linking 3PIDs creates a mapping from a 3PID to a user ID. This mapping
|
||||
can then be used by Matrix users in order to discover the user IDs of
|
||||
their contacts. In order to ensure that the mapping from 3PID to user ID
|
||||
is genuine, a globally federated cluster of trusted "identity servers"
|
||||
(IS) are used to verify the 3PID and persist and replicate the mappings.
|
||||
|
||||
Usage of an IS is not required in order for a client application to be
|
||||
part of the Matrix ecosystem. However, without one clients will not be
|
||||
able to look up user IDs using 3PIDs.
|
||||
|
||||
### Profiles
|
||||
|
||||
Users may publish arbitrary key/value data associated with their account
|
||||
- such as a human-readable display name, a profile photo URL, contact
|
||||
information (email address, phone numbers, website URLs etc).
|
||||
|
||||
### Private User Data
|
||||
|
||||
Users may also store arbitrary private key/value data in their account -
|
||||
such as client preferences, or server configuration settings which lack
|
||||
any other dedicated API. The API is symmetrical to managing Profile
|
||||
data.
|
||||
|
||||
## Common concepts
|
||||
|
||||
Various things are common throughout all of the Matrix APIs. They are
|
||||
documented here.
|
||||
|
||||
### Namespacing
|
||||
|
||||
Namespacing helps prevent conflicts between multiple applications and
|
||||
the specification itself. Where namespacing is used, `m.` prefixes are
|
||||
used by the specification to indicate that the field is controlled by
|
||||
the specification. Custom or non-specified namespaces used in the wild
|
||||
MUST use the Java package naming convention to prevent conflicts.
|
||||
|
||||
As an example, event types defined in the specification are namespaced
|
||||
under the special `m.` prefix, however any client can send a custom
|
||||
event type, such as `com.example.game.score` (assuming the client has
|
||||
rights to the `com.example` namespace) without needing to put the event
|
||||
into the `m.` namespace.
|
||||
|
||||
### Timestamps
|
||||
|
||||
Unless otherwise stated, timestamps are measured as milliseconds since
|
||||
the Unix epoch. Throughout the specification this may be referred to as
|
||||
POSIX, Unix, or just "time in milliseconds".
|
||||
|
||||
## Room Versions
|
||||
|
||||
Rooms are central to how Matrix operates, and have strict rules for what
|
||||
is allowed to be contained within them. Rooms can also have various
|
||||
algorithms that handle different tasks, such as what to do when two or
|
||||
more events collide in the underlying DAG. To allow rooms to be improved
|
||||
upon through new algorithms or rules, "room versions" are employed to
|
||||
manage a set of expectations for each room. New room versions are
|
||||
assigned as needed.
|
||||
|
||||
There is no implicit ordering or hierarchy to room versions, and their
|
||||
principles are immutable once placed in the specification. Although
|
||||
there is a recommended set of versions, some rooms may benefit from
|
||||
features introduced by other versions. Rooms move between different
|
||||
versions by "upgrading" to the desired version. Due to versions not
|
||||
being ordered or hierarchical, this means a room can "upgrade" from
|
||||
version 2 to version 1, if it is so desired.
|
||||
|
||||
### Room version grammar
|
||||
|
||||
Room versions are used to change properties of rooms that may not be
|
||||
compatible with other servers. For example, changing the rules for event
|
||||
authorization would cause older servers to potentially end up in a
|
||||
split-brain situation due to not understanding the new rules.
|
||||
|
||||
A room version is defined as a string of characters which MUST NOT
|
||||
exceed 32 codepoints in length. Room versions MUST NOT be empty and
|
||||
SHOULD contain only the characters `a-z`, `0-9`, `.`, and `-`.
|
||||
|
||||
Room versions are not intended to be parsed and should be treated as
|
||||
opaque identifiers. Room versions consisting only of the characters
|
||||
`0-9` and `.` are reserved for future versions of the Matrix protocol.
|
||||
|
||||
The complete grammar for a legal room version is:
|
||||
|
||||
room_version = 1*room_version_char
|
||||
room_version_char = DIGIT
|
||||
/ %x61-7A ; a-z
|
||||
/ "-" / "."
|
||||
|
||||
Examples of valid room versions are:
|
||||
|
||||
- `1` (would be reserved by the Matrix protocol)
|
||||
- `1.2` (would be reserved by the Matrix protocol)
|
||||
- `1.2-beta`
|
||||
- `com.example.version`
|
||||
|
||||
### Complete list of room versions
|
||||
|
||||
Room versions are divided into two distinct groups: stable and unstable.
|
||||
Stable room versions may be used by rooms safely. Unstable room versions
|
||||
are everything else which is either not listed in the specification or
|
||||
flagged as unstable for some other reason. Versions can switch between
|
||||
stable and unstable periodically for a variety of reasons, including
|
||||
discovered security vulnerabilities and age.
|
||||
|
||||
Clients should not ask room administrators to upgrade their rooms if the
|
||||
room is running a stable version. Servers SHOULD use room version 6 as
|
||||
the default room version when creating new rooms.
|
||||
|
||||
The available room versions are:
|
||||
|
||||
- [Version 1](rooms/v1.html) - **Stable**. The current version of most
|
||||
rooms.
|
||||
- [Version 2](rooms/v2.html) - **Stable**. Implements State Resolution
|
||||
Version 2.
|
||||
- [Version 3](rooms/v3.html) - **Stable**. Introduces events whose IDs
|
||||
are the event's hash.
|
||||
- [Version 4](rooms/v4.html) - **Stable**. Builds on v3 by using
|
||||
URL-safe base64 for event IDs.
|
||||
- [Version 5](rooms/v5.html) - **Stable**. Introduces enforcement of
|
||||
signing key validity periods.
|
||||
- [Version 6](rooms/v6.html) - **Stable**. Alters several
|
||||
authorization rules for events.
|
||||
|
||||
## Specification Versions
|
||||
|
||||
The specification for each API is versioned in the form `rX.Y.Z`.
|
||||
- A change to `X` reflects a breaking change: a client implemented
|
||||
against `r1.0.0` may need changes to work with a server which
|
||||
supports (only) `r2.0.0`.
|
||||
- A change to `Y` represents a change which is backwards-compatible
|
||||
for existing clients, but not necessarily existing servers: a client
|
||||
implemented against `r1.1.0` will work without changes against a
|
||||
server which supports `r1.2.0`; but a client which requires `r1.2.0`
|
||||
may not work correctly with a server which implements only `r1.1.0`.
|
||||
- A change to `Z` represents a change which is backwards-compatible on
|
||||
both sides. Typically this implies a clarification to the
|
||||
specification, rather than a change which must be implemented.
|
||||
|
||||
## License
|
||||
|
||||
The Matrix specification is licensed under the [Apache License, Version
|
||||
2.0](http://www.apache.org/licenses/LICENSE-2.0).
|
@ -0,0 +1,8 @@
|
||||
{{ define "main"}}
|
||||
<main id="main">
|
||||
<div>
|
||||
<h1 id="title">Not found</h1>
|
||||
<p>This page doesn't exist. Try going back to the <a href="{{ "/" | relURL }}">main page for the Matrix Specification</a>.</p>
|
||||
</div>
|
||||
</main>
|
||||
{{ end }}
|
@ -0,0 +1,4 @@
|
||||
<div class="td-content">
|
||||
<h1>{{ .Title }}</h1>
|
||||
{{ .Content }}
|
||||
</div>
|
@ -0,0 +1,27 @@
|
||||
<!doctype html>
|
||||
<html lang="{{ .Site.Language.Lang }}" class="no-js">
|
||||
<head>
|
||||
{{ partial "head.html" . }}
|
||||
</head>
|
||||
<body class="td-{{ .Kind }}">
|
||||
<header>
|
||||
{{ partial "navbar.html" . }}
|
||||
</header>
|
||||
<div class="container-fluid td-outer">
|
||||
<div class="td-main">
|
||||
<div class="row flex-xl-nowrap">
|
||||
<div class="col-12 col-md-3 col-xl-3 td-sidebar d-print-none">
|
||||
{{ partial "sidebar.html" . }}
|
||||
</div>
|
||||
<main class="col-12 col-md-9 col-xl-7 pl-md-5" role="main">
|
||||
{{ partial "version-banner.html" . }}
|
||||
{{ partial "breadcrumb.html" . }}
|
||||
{{ block "main" . }}{{ end }}
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
{{ partial "footer.html" . }}
|
||||
</div>
|
||||
{{ partial "scripts.html" . }}
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,7 @@
|
||||
{{ define "main" }}
|
||||
<div class="td-content">
|
||||
<h1>{{ .Title }}</h1>
|
||||
{{ with .Params.description }}<div class="lead">{{ . | markdownify }}</div>{{ end }}
|
||||
{{ .Content }}
|
||||
</div>
|
||||
{{ end }}
|
@ -0,0 +1,28 @@
|
||||
{{/*
|
||||
|
||||
A copy of the breadcrumb.html partial in Docsy, modified
|
||||
to:
|
||||
* omit breadcrumbs when this is the homepage
|
||||
* otherwise, include the homepage in the breadcrumbs
|
||||
|
||||
*/}}
|
||||
|
||||
{{ if not .IsHome }}
|
||||
<nav aria-label="breadcrumb" class="d-none d-md-block d-print-none">
|
||||
<ol class="breadcrumb spb-1">
|
||||
{{ template "breadcrumbnav" (dict "p1" . "p2" .) }}
|
||||
</ol>
|
||||
</nav >
|
||||
{{ end }}
|
||||
|
||||
{{ define "breadcrumbnav" }}
|
||||
{{ if .p1.Parent }}
|
||||
{{ template "breadcrumbnav" (dict "p1" .p1.Parent "p2" .p2 ) }}
|
||||
{{ else if not .p1.IsHome }}
|
||||
{{ template "breadcrumbnav" (dict "p1" .p1.Site.Home "p2" .p2 ) }}
|
||||
{{ end }}
|
||||
{{ $isActive := eq .p1 .p2 }}
|
||||
<li class="breadcrumb-item{{ if $isActive }} active{{ end }}" {{ if $isActive }}aria-current="page"{{ end }}>
|
||||
<a href="{{ .p1.Permalink }}">{{ .p1.LinkTitle }}</a>
|
||||
</li>
|
||||
{{ end }}
|
@ -0,0 +1,33 @@
|
||||
{{ $links := .Site.Params.links }}
|
||||
<footer class="py-5 row d-print-none">
|
||||
<div class="container-fluid mx-sm-5">
|
||||
<div class="row">
|
||||
<div class="col-12 text-center text-xs-center order-sm-3">
|
||||
{{ with $links }}
|
||||
{{ with index . "developer"}}
|
||||
{{ template "footer-links-block" . }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12 text-center py-2 order-sm-3">
|
||||
{{ with .Site.Params.copyright }}<small>© {{ now.Year}} {{ .}}</small>{{ end }}
|
||||
{{ if not .Site.Params.ui.footer_about_disable }}
|
||||
{{ with .Site.GetPage "about" }}<p class="mt-2"><a href="{{ .RelPermalink }}">{{ .Title }}</a></p>{{ end }}
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
{{ define "footer-links-block" }}
|
||||
<ul class="list-inline mb-0">
|
||||
{{ range . }}
|
||||
<li class="list-inline-item mx-2 h3" data-toggle="tooltip" data-placement="top" title="{{ .name }}" aria-label="{{ .name }}">
|
||||
<a class="text-dark" target="_blank" rel="noopener noreferrer" href="{{ .url }}">
|
||||
<i class="{{ .icon }}"></i>
|
||||
</a>
|
||||
</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
{{ end }}
|
@ -0,0 +1,17 @@
|
||||
{{/*
|
||||
|
||||
This template is included at the end of each page's `<body>`.
|
||||
|
||||
We're using it here to:
|
||||
|
||||
1) include the JS that generates the table of contents. It would be better
|
||||
to generate the table of contents as part of the Hugo build process, but
|
||||
that doesn't work nicely with the way we want to author client-server modules
|
||||
as separate files.
|
||||
|
||||
2) highlight and scroll the ToC in the sidebar to match the place we are at
|
||||
in the document.
|
||||
|
||||
*/}}
|
||||
|
||||
<script defer language="javascript" type="text/javascript" src="{{ "js/toc.js" | urlize | relURL }}"></script>
|
@ -0,0 +1,18 @@
|
||||
{{/*
|
||||
|
||||
This template is included at the end of each page's `<head>`.
|
||||
|
||||
We're using it here to include the custom CSS for the Matrix spec.
|
||||
|
||||
*/}}
|
||||
|
||||
{{ $scss := "scss/custom.scss"}}
|
||||
{{ if .Site.IsServer }}
|
||||
{{/* Note the missing postCSS. This makes it snappier to develop in Chrome, but makes it look sub-optimal in other browsers. */}}
|
||||
{{ $css := resources.Get $scss | toCSS (dict "enableSourceMap" true) }}
|
||||
<link href="{{ $css.RelPermalink }}" rel="stylesheet">
|
||||
{{ else }}
|
||||
{{ $css := resources.Get $scss | toCSS (dict "enableSourceMap" false) | postCSS | minify | fingerprint }}
|
||||
<link rel="preload" href="{{ $css.RelPermalink }}" as="style">
|
||||
<link href="{{ $css.RelPermalink }}" rel="stylesheet" integrity="{{ $css.Data.integrity }}">
|
||||
{{ end }}
|
@ -0,0 +1,54 @@
|
||||
{{/*
|
||||
|
||||
A copy of the navbar.html partial in Docsy, only modified
|
||||
to include the spec version, which is calculated using an
|
||||
inline `version-string` partial.
|
||||
|
||||
*/}}
|
||||
|
||||
{{ $cover := .HasShortcode "blocks/cover" }}
|
||||
<nav class="js-navbar-scroll navbar navbar-expand navbar-light {{ if $cover}} td-navbar-cover {{ end }}flex-column flex-md-row td-navbar">
|
||||
<a class="navbar-brand" href="{{ .Site.Home.RelPermalink }}">
|
||||
<span class="navbar-logo">{{ with resources.Get "icons/logo.svg" }}{{ ( . | minify).Content | safeHTML }}{{ end }}</span><span class="font-weight-bold">specification</span><span class="navbar-version"> — {{ partial "version-string" . }}</span>
|
||||
</a>
|
||||
<div class="td-navbar-nav-scroll ml-md-auto" id="main_navbar">
|
||||
<ul class="navbar-nav mt-2 mt-lg-0">
|
||||
|
||||
<li class="nav-item mr-4 mb-2 mb-lg-0"><a href="https://matrix.org/foundation/">Foundation</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item mr-4 mb-2 mb-lg-0"><a href="https://matrix.org/faq/">FAQs</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item mr-4 mb-2 mb-lg-0"><a href="https://matrix.org/blog/posts">Blog</a>
|
||||
</li>
|
||||
|
||||
{{ if .Site.Params.versions }}
|
||||
<li class="nav-item dropdown d-none d-lg-block">
|
||||
{{ partial "navbar-version-selector.html" . }}
|
||||
</li>
|
||||
{{ end }}
|
||||
{{ if (gt (len .Site.Home.Translations) 0) }}
|
||||
<li class="nav-item dropdown d-none d-lg-block">
|
||||
{{ partial "navbar-lang-selector.html" . }}
|
||||
</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="navbar-nav d-none d-lg-block">{{ partial "search-input.html" . }}</div>
|
||||
</nav>
|
||||
|
||||
|
||||
{{ define "partials/version-string" }}
|
||||
{{ $ret := "unstable version"}}
|
||||
|
||||
{{ $status := .Site.Params.version.status }}
|
||||
|
||||
{{ if ne $status "unstable"}}
|
||||
{{ $version_pieces := slice .Site.Params.version.major_version .Site.Params.version.minor_version .Site.Params.version.patch_version}}
|
||||
{{ $ret = delimit $version_pieces "." }}
|
||||
{{ $ret = delimit (slice "version" $ret) " " }}
|
||||
{{ end }}
|
||||
|
||||
{{ return $ret }}
|
||||
{{ end }}
|
@ -0,0 +1,63 @@
|
||||
{{/*
|
||||
|
||||
A copy of the sidebar-tree.html partial in Docsy, with a few small modifications:
|
||||
|
||||
* include `div#toc` for the ToC
|
||||
* start the sidebar at the root (homepage) since for us that is the Matrix
|
||||
overview page
|
||||
* omit module pages, which we don't want to be directly accessible
|
||||
(we only use them as raw material for the client-server spec)
|
||||
|
||||
*/}}
|
||||
|
||||
{{/* We cache this partial for bigger sites and set the active class client side. */}}
|
||||
{{ $shouldDelayActive := ge (len .Site.Pages) 2000 }}
|
||||
<div id="td-sidebar-menu" class="td-sidebar__inner{{ if $shouldDelayActive }} d-none{{ end }}">
|
||||
<div id="content-mobile">
|
||||
<form class="td-sidebar__search d-flex align-items-center">
|
||||
{{ partial "search-input.html" . }}
|
||||
<button class="btn btn-link td-sidebar__toggle d-md-none p-0 ml-3 fas fa-bars" type="button" data-toggle="collapse" data-target="#td-section-nav" aria-controls="td-docs-nav" aria-expanded="false" aria-label="Toggle section navigation">
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<div id="content-desktop"></div>
|
||||
<nav class="collapse td-sidebar-nav" id="td-section-nav">
|
||||
{{ template "section-tree-nav-section" (dict "page" . "section" .Site.Home.CurrentSection "delayActive" $shouldDelayActive "indent" 0) }}
|
||||
<hr/>
|
||||
<div id = "toc"></div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
{{ define "section-tree-nav-section" }}
|
||||
{{ $s := .section }}
|
||||
{{ $p := .page }}
|
||||
{{ $shouldDelayActive := .delayActive }}
|
||||
{{ $indent := .indent }}
|
||||
{{ $active := eq $p.RelPermalink $s.RelPermalink }}
|
||||
{{ $show := or ($p.IsAncestor $s) ($p.IsDescendant $s) }}
|
||||
{{ $sid := $s.RelPermalink | anchorize }}
|
||||
<ul class="td-sidebar-nav__section pr-md-3">
|
||||
<li class="td-sidebar-nav__section-title">
|
||||
<a href="{{ $s.RelPermalink }}" class="align-left pl-0 pr-2{{ if not $show }} collapsed{{ end }}{{ if $active}} active{{ end }} td-sidebar-link td-sidebar-link__section indent-{{$indent}}">{{ $s.LinkTitle }}</a>
|
||||
{{ $pages := where (union $s.Pages $s.Sections).ByWeight ".Params.toc_hide" "!=" true }}
|
||||
{{ $pages = where $pages "Type" "!=" "module"}}
|
||||
{{ $pages := $pages | first 50 }}
|
||||
{{ if gt (len $pages) 0 }}
|
||||
<ul>
|
||||
{{ range $pages }}
|
||||
{{ if .IsPage }}
|
||||
{{ $mid := printf "m-%s" (.RelPermalink | anchorize) }}
|
||||
{{ $active := eq . $p }}
|
||||
<li class="collapse {{ if $show }}show{{ end }}" id="{{ $sid }}">
|
||||
<a class="td-sidebar-link td-sidebar-link__page {{ if and (not $shouldDelayActive) $active }} active{{ end }} indent-{{add $indent 1}}" id="{{ $mid }}" href="{{ .RelPermalink }}">{{ .LinkTitle }}</a>
|
||||
</li>
|
||||
{{ else }}
|
||||
{{ $indent := add $indent 1 }}
|
||||
{{ template "section-tree-nav-section" (dict "page" $p "section" . "indent" $indent) }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</ul>
|
||||
{{ end }}
|
||||
</li>
|
||||
</ul>
|
||||
{{ end }}
|
@ -0,0 +1,22 @@
|
||||
{{/*
|
||||
|
||||
A modified copy of the version-banner.html partial in Docsy.
|
||||
|
||||
*/}}
|
||||
|
||||
|
||||
{{ $color := "primary" }}
|
||||
{{ $status := .Site.Params.version.status }}
|
||||
{{ $latest_version := .Site.Params.version.current_version_url }}
|
||||
{{ if eq $status "unstable"}}
|
||||
<div class="pageinfo pageinfo-{{ $color }} pageinfo-unstable">
|
||||
<p>You're looking at an unstable version of this specification.
|
||||
Unstable specifications may change at any time without notice.</p>
|
||||
<p><a href="{{ $latest_version | safeURL }}">Switch to the current stable release</a>.</p>
|
||||
</div>
|
||||
{{ else if eq $status "historical" }}
|
||||
<div class="pageinfo pageinfo-{{ $color }}">
|
||||
<p>You're looking at an old version of this specification.<p>
|
||||
<p><a href="{{ $latest_version | safeURL }}">Switch to the current stable release</a>.</p>
|
||||
</div>
|
||||
{{ end }}
|
@ -0,0 +1,978 @@
|
||||
{
|
||||
"name": "matrix-spec",
|
||||
"version": "0.0.1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@nodelib/fs.scandir": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz",
|
||||
"integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@nodelib/fs.stat": "2.0.3",
|
||||
"run-parallel": "^1.1.9"
|
||||
}
|
||||
},
|
||||
"@nodelib/fs.stat": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz",
|
||||
"integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==",
|
||||
"dev": true
|
||||
},
|
||||
"@nodelib/fs.walk": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz",
|
||||
"integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@nodelib/fs.scandir": "2.1.3",
|
||||
"fastq": "^1.6.0"
|
||||
}
|
||||
},
|
||||
"@types/color-name": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
|
||||
"integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
|
||||
"dev": true
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
||||
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
|
||||
"dev": true
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "^1.9.0"
|
||||
}
|
||||
},
|
||||
"anymatch": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz",
|
||||
"integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"normalize-path": "^3.0.0",
|
||||
"picomatch": "^2.0.4"
|
||||
}
|
||||
},
|
||||
"argparse": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"sprintf-js": "~1.0.2"
|
||||
}
|
||||
},
|
||||
"array-union": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
|
||||
"integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
|
||||
"dev": true
|
||||
},
|
||||
"at-least-node": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
|
||||
"integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
|
||||
"dev": true
|
||||
},
|
||||
"autoprefixer": {
|
||||
"version": "9.8.6",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.6.tgz",
|
||||
"integrity": "sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"browserslist": "^4.12.0",
|
||||
"caniuse-lite": "^1.0.30001109",
|
||||
"colorette": "^1.2.1",
|
||||
"normalize-range": "^0.1.2",
|
||||
"num2fraction": "^1.2.2",
|
||||
"postcss": "^7.0.32",
|
||||
"postcss-value-parser": "^4.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"postcss": {
|
||||
"version": "7.0.32",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.32.tgz",
|
||||
"integrity": "sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "^2.4.2",
|
||||
"source-map": "^0.6.1",
|
||||
"supports-color": "^6.1.0"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
|
||||
"integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-flag": "^3.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"binary-extensions": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz",
|
||||
"integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==",
|
||||
"dev": true
|
||||
},
|
||||
"braces": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
||||
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fill-range": "^7.0.1"
|
||||
}
|
||||
},
|
||||
"browserslist": {
|
||||
"version": "4.14.0",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.0.tgz",
|
||||
"integrity": "sha512-pUsXKAF2lVwhmtpeA3LJrZ76jXuusrNyhduuQs7CDFf9foT4Y38aQOserd2lMe5DSSrjf3fx34oHwryuvxAUgQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"caniuse-lite": "^1.0.30001111",
|
||||
"electron-to-chromium": "^1.3.523",
|
||||
"escalade": "^3.0.2",
|
||||
"node-releases": "^1.1.60"
|
||||
}
|
||||
},
|
||||
"caller-callsite": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz",
|
||||
"integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"callsites": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"caller-path": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz",
|
||||
"integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"caller-callsite": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"callsites": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz",
|
||||
"integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=",
|
||||
"dev": true
|
||||
},
|
||||
"camelcase": {
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
|
||||
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
|
||||
"dev": true
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30001115",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001115.tgz",
|
||||
"integrity": "sha512-NZrG0439ePYna44lJX8evHX2L7Z3/z3qjVLnHgbBb/duNEnGo348u+BQS5o4HTWcrb++100dHFrU36IesIrC1Q==",
|
||||
"dev": true
|
||||
},
|
||||
"chalk": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^3.2.1",
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
"supports-color": "^5.3.0"
|
||||
}
|
||||
},
|
||||
"chokidar": {
|
||||
"version": "3.4.2",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.2.tgz",
|
||||
"integrity": "sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"anymatch": "~3.1.1",
|
||||
"braces": "~3.0.2",
|
||||
"fsevents": "~2.1.2",
|
||||
"glob-parent": "~5.1.0",
|
||||
"is-binary-path": "~2.1.0",
|
||||
"is-glob": "~4.0.1",
|
||||
"normalize-path": "~3.0.0",
|
||||
"readdirp": "~3.4.0"
|
||||
}
|
||||
},
|
||||
"cliui": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
|
||||
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"string-width": "^4.2.0",
|
||||
"strip-ansi": "^6.0.0",
|
||||
"wrap-ansi": "^6.2.0"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "1.1.3"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
|
||||
"dev": true
|
||||
},
|
||||
"colorette": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz",
|
||||
"integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==",
|
||||
"dev": true
|
||||
},
|
||||
"cosmiconfig": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz",
|
||||
"integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"import-fresh": "^2.0.0",
|
||||
"is-directory": "^0.3.1",
|
||||
"js-yaml": "^3.13.1",
|
||||
"parse-json": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"decamelize": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
|
||||
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
|
||||
"dev": true
|
||||
},
|
||||
"dependency-graph": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.9.0.tgz",
|
||||
"integrity": "sha512-9YLIBURXj4DJMFALxXw9K3Y3rwb5Fk0X5/8ipCzaN84+gKxoHK43tVKRNakCQbiEx07E8Uwhuq21BpUagFhZ8w==",
|
||||
"dev": true
|
||||
},
|
||||
"dir-glob": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
|
||||
"integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"path-type": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.3.534",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.534.tgz",
|
||||
"integrity": "sha512-7x2S3yUrspNHQOoPk+Eo+iHViSiJiEGPI6BpmLy1eT2KRNGCkBt/NUYqjfXLd1DpDCQp7n3+LfA1RkbG+LqTZQ==",
|
||||
"dev": true
|
||||
},
|
||||
"emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||
"dev": true
|
||||
},
|
||||
"error-ex": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
|
||||
"integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-arrayish": "^0.2.1"
|
||||
}
|
||||
},
|
||||
"escalade": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.0.2.tgz",
|
||||
"integrity": "sha512-gPYAU37hYCUhW5euPeR+Y74F7BL+IBsV93j5cvGriSaD1aG6MGsqsV1yamRdrWrb2j3aiZvb0X+UBOWpx3JWtQ==",
|
||||
"dev": true
|
||||
},
|
||||
"escape-string-regexp": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
|
||||
"dev": true
|
||||
},
|
||||
"esprima": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
|
||||
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
|
||||
"dev": true
|
||||
},
|
||||
"fast-glob": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.4.tgz",
|
||||
"integrity": "sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@nodelib/fs.stat": "^2.0.2",
|
||||
"@nodelib/fs.walk": "^1.2.3",
|
||||
"glob-parent": "^5.1.0",
|
||||
"merge2": "^1.3.0",
|
||||
"micromatch": "^4.0.2",
|
||||
"picomatch": "^2.2.1"
|
||||
}
|
||||
},
|
||||
"fastq": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.8.0.tgz",
|
||||
"integrity": "sha512-SMIZoZdLh/fgofivvIkmknUXyPnvxRE3DhtZ5Me3Mrsk5gyPL42F0xr51TdRXskBxHfMp+07bcYzfsYEsSQA9Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"reusify": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"fill-range": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"find-up": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
|
||||
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"locate-path": "^5.0.0",
|
||||
"path-exists": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"fs-extra": {
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz",
|
||||
"integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"at-least-node": "^1.0.0",
|
||||
"graceful-fs": "^4.2.0",
|
||||
"jsonfile": "^6.0.1",
|
||||
"universalify": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"fsevents": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
|
||||
"integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"get-caller-file": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
|
||||
"dev": true
|
||||
},
|
||||
"get-stdin": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz",
|
||||
"integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==",
|
||||
"dev": true
|
||||
},
|
||||
"glob-parent": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
|
||||
"integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-glob": "^4.0.1"
|
||||
}
|
||||
},
|
||||
"globby": {
|
||||
"version": "11.0.1",
|
||||
"resolved": "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz",
|
||||
"integrity": "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"array-union": "^2.1.0",
|
||||
"dir-glob": "^3.0.1",
|
||||
"fast-glob": "^3.1.1",
|
||||
"ignore": "^5.1.4",
|
||||
"merge2": "^1.3.0",
|
||||
"slash": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"graceful-fs": {
|
||||
"version": "4.2.4",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
|
||||
"integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
|
||||
"dev": true
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
|
||||
"dev": true
|
||||
},
|
||||
"ignore": {
|
||||
"version": "5.1.8",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz",
|
||||
"integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==",
|
||||
"dev": true
|
||||
},
|
||||
"import-cwd": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz",
|
||||
"integrity": "sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"import-from": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"import-fresh": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz",
|
||||
"integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"caller-path": "^2.0.0",
|
||||
"resolve-from": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"import-from": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz",
|
||||
"integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"resolve-from": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"is-arrayish": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
|
||||
"integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
|
||||
"dev": true
|
||||
},
|
||||
"is-binary-path": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"binary-extensions": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"is-directory": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz",
|
||||
"integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=",
|
||||
"dev": true
|
||||
},
|
||||
"is-extglob": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
|
||||
"dev": true
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||
"dev": true
|
||||
},
|
||||
"is-glob": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
|
||||
"integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-extglob": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"is-number": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"dev": true
|
||||
},
|
||||
"js-yaml": {
|
||||
"version": "3.14.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz",
|
||||
"integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"argparse": "^1.0.7",
|
||||
"esprima": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"json-parse-better-errors": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
|
||||
"integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
|
||||
"dev": true
|
||||
},
|
||||
"jsonfile": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz",
|
||||
"integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.6",
|
||||
"universalify": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"locate-path": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
|
||||
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"p-locate": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.20",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
|
||||
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
|
||||
"dev": true
|
||||
},
|
||||
"log-symbols": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz",
|
||||
"integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"merge2": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
||||
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
|
||||
"dev": true
|
||||
},
|
||||
"micromatch": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz",
|
||||
"integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"braces": "^3.0.1",
|
||||
"picomatch": "^2.0.5"
|
||||
}
|
||||
},
|
||||
"node-releases": {
|
||||
"version": "1.1.60",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.60.tgz",
|
||||
"integrity": "sha512-gsO4vjEdQaTusZAEebUWp2a5d7dF5DYoIpDG7WySnk7BuZDW+GPpHXoXXuYawRBr/9t5q54tirPz79kFIWg4dA==",
|
||||
"dev": true
|
||||
},
|
||||
"normalize-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
||||
"dev": true
|
||||
},
|
||||
"normalize-range": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
|
||||
"integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=",
|
||||
"dev": true
|
||||
},
|
||||
"num2fraction": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz",
|
||||
"integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=",
|
||||
"dev": true
|
||||
},
|
||||
"p-limit": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
|
||||
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"p-try": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"p-locate": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
|
||||
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"p-limit": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"p-try": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
|
||||
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
|
||||
"dev": true
|
||||
},
|
||||
"parse-json": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
|
||||
"integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"error-ex": "^1.3.1",
|
||||
"json-parse-better-errors": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"path-exists": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
|
||||
"dev": true
|
||||
},
|
||||
"path-type": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
|
||||
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
|
||||
"dev": true
|
||||
},
|
||||
"picomatch": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
|
||||
"integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
|
||||
"dev": true
|
||||
},
|
||||
"pify": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
|
||||
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
|
||||
"dev": true
|
||||
},
|
||||
"postcss": {
|
||||
"version": "7.0.32",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.32.tgz",
|
||||
"integrity": "sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "^2.4.2",
|
||||
"source-map": "^0.6.1",
|
||||
"supports-color": "^6.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"supports-color": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
|
||||
"integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-flag": "^3.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"postcss-cli": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/postcss-cli/-/postcss-cli-7.1.2.tgz",
|
||||
"integrity": "sha512-3mlEmN1v2NVuosMWZM2tP8bgZn7rO5PYxRRrXtdSyL5KipcgBDjJ9ct8/LKxImMCJJi3x5nYhCGFJOkGyEqXBQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "^4.0.0",
|
||||
"chokidar": "^3.3.0",
|
||||
"dependency-graph": "^0.9.0",
|
||||
"fs-extra": "^9.0.0",
|
||||
"get-stdin": "^8.0.0",
|
||||
"globby": "^11.0.0",
|
||||
"postcss": "^7.0.0",
|
||||
"postcss-load-config": "^2.0.0",
|
||||
"postcss-reporter": "^6.0.0",
|
||||
"pretty-hrtime": "^1.0.3",
|
||||
"read-cache": "^1.0.0",
|
||||
"yargs": "^15.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
|
||||
"integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/color-name": "^1.1.1",
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"chalk": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
|
||||
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"dev": true
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"postcss-load-config": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.1.0.tgz",
|
||||
"integrity": "sha512-4pV3JJVPLd5+RueiVVB+gFOAa7GWc25XQcMp86Zexzke69mKf6Nx9LRcQywdz7yZI9n1udOxmLuAwTBypypF8Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cosmiconfig": "^5.0.0",
|
||||
"import-cwd": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"postcss-reporter": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/postcss-reporter/-/postcss-reporter-6.0.1.tgz",
|
||||
"integrity": "sha512-LpmQjfRWyabc+fRygxZjpRxfhRf9u/fdlKf4VHG4TSPbV2XNsuISzYW1KL+1aQzx53CAppa1bKG4APIB/DOXXw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "^2.4.1",
|
||||
"lodash": "^4.17.11",
|
||||
"log-symbols": "^2.2.0",
|
||||
"postcss": "^7.0.7"
|
||||
}
|
||||
},
|
||||
"postcss-value-parser": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz",
|
||||
"integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==",
|
||||
"dev": true
|
||||
},
|
||||
"pretty-hrtime": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz",
|
||||
"integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=",
|
||||
"dev": true
|
||||
},
|
||||
"read-cache": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||
"integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"pify": "^2.3.0"
|
||||
}
|
||||
},
|
||||
"readdirp": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz",
|
||||
"integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"picomatch": "^2.2.1"
|
||||
}
|
||||
},
|
||||
"require-directory": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
|
||||
"dev": true
|
||||
},
|
||||
"require-main-filename": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
|
||||
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
|
||||
"dev": true
|
||||
},
|
||||
"resolve-from": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
|
||||
"integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
|
||||
"dev": true
|
||||
},
|
||||
"reusify": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
|
||||
"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
|
||||
"dev": true
|
||||
},
|
||||
"run-parallel": {
|
||||
"version": "1.1.9",
|
||||
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz",
|
||||
"integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==",
|
||||
"dev": true
|
||||
},
|
||||
"set-blocking": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
||||
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
|
||||
"dev": true
|
||||
},
|
||||
"slash": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
|
||||
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
|
||||
"dev": true
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true
|
||||
},
|
||||
"sprintf-js": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
|
||||
"dev": true
|
||||
},
|
||||
"string-width": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
|
||||
"integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
|
||||
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-flag": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-number": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"universalify": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz",
|
||||
"integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==",
|
||||
"dev": true
|
||||
},
|
||||
"which-module": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
|
||||
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
|
||||
"dev": true
|
||||
},
|
||||
"wrap-ansi": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
|
||||
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
"string-width": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
|
||||
"integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/color-name": "^1.1.1",
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"y18n": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
|
||||
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
|
||||
"dev": true
|
||||
},
|
||||
"yargs": {
|
||||
"version": "15.4.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
|
||||
"integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cliui": "^6.0.0",
|
||||
"decamelize": "^1.2.0",
|
||||
"find-up": "^4.1.0",
|
||||
"get-caller-file": "^2.0.1",
|
||||
"require-directory": "^2.1.1",
|
||||
"require-main-filename": "^2.0.0",
|
||||
"set-blocking": "^2.0.0",
|
||||
"string-width": "^4.2.0",
|
||||
"which-module": "^2.0.0",
|
||||
"y18n": "^4.0.0",
|
||||
"yargs-parser": "^18.1.2"
|
||||
}
|
||||
},
|
||||
"yargs-parser": {
|
||||
"version": "18.1.3",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
|
||||
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"camelcase": "^5.0.0",
|
||||
"decamelize": "^1.2.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "matrix-spec",
|
||||
"version": "0.0.1",
|
||||
"description": "Hugo theme for the Matrix specification.",
|
||||
"main": "none.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/matrix-org/matrix-doc.git"
|
||||
},
|
||||
"author": "",
|
||||
"license": "Apache-2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/matrix-org/matrix-doc"
|
||||
},
|
||||
"homepage": "https://github.com/matrix-org/matrix-doc#readme",
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^9.8.6",
|
||||
"postcss-cli": "^7.1.2"
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
@ -0,0 +1,301 @@
|
||||
/*
|
||||
Account for id attributes that are in the sidebar nav
|
||||
*/
|
||||
function populateIds() {
|
||||
const navItems = document.querySelectorAll(".td-sidebar-nav li");
|
||||
return Array.from(navItems).map(item => item.id).filter(id => id != "");
|
||||
}
|
||||
|
||||
/*
|
||||
Given an ID and an array of IDs, return s version of the original ID that's
|
||||
not equal to any of the IDs in the array.
|
||||
*/
|
||||
function uniquifyHeadingId(id, uniqueIDs) {
|
||||
const baseId = id;
|
||||
let counter = 0;
|
||||
while (uniqueIDs.includes(id)) {
|
||||
counter = counter + 1;
|
||||
id = baseId + "-" + counter.toString();
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
/*
|
||||
Given an array of heading nodes, ensure they all have unique IDs.
|
||||
|
||||
We have to do this mostly because of client-server modules, which are
|
||||
rendered separately then glued together with a template.
|
||||
Because heading IDs are generated in rendering, this means they can and will
|
||||
end up with duplicate IDs.
|
||||
*/
|
||||
function uniquifyHeadingIds(headings) {
|
||||
const uniqueIDs = populateIds();
|
||||
for (let heading of headings) {
|
||||
const uniqueID = uniquifyHeadingId(heading.id, uniqueIDs);
|
||||
uniqueIDs.push(uniqueID);
|
||||
heading.id = uniqueID;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
The document contains "normal" headings, and these have corresponding items
|
||||
in the ToC.
|
||||
|
||||
The document might also contain H1 headings that act as titles for blocks of
|
||||
rendered data, like HTTP APIs or event schemas. Unlike "normal" headings,
|
||||
these headings don't appear in the ToC. But they do have anchor IDs to enable
|
||||
links to them. When someone follows a link to one of these "rendered data"
|
||||
headings we want to scroll the ToC to the item corresponding to the "normal"
|
||||
heading preceding the "rendered data" heading we have visited.
|
||||
|
||||
To support this we need to add `data` attributes to ToC items.
|
||||
These attributes identify which "rendered data" headings live underneath
|
||||
the heading corresponding to that ToC item.
|
||||
*/
|
||||
function setTocItemChildren(toc, headings) {
|
||||
let tocEntryForHeading = null;
|
||||
for (const heading of headings) {
|
||||
// H1 headings are rendered-data headings
|
||||
if (heading.tagName !== "H1") {
|
||||
tocEntryForHeading = document.querySelector(`nav li a[href="#${heading.id}"]`);
|
||||
} else {
|
||||
// on the ToC entry for the parent heading,
|
||||
// set a data-* attribute whose name is the child's fragment ID
|
||||
tocEntryForHeading.setAttribute(`data-${heading.id}`, "true");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Generate a table of contents based on the headings in the document.
|
||||
*/
|
||||
function makeToc() {
|
||||
|
||||
// make the title from the H1
|
||||
const h1 = document.body.querySelector("h1");
|
||||
const title = document.createElement("a");
|
||||
title.id = "toc-title";
|
||||
title.setAttribute("href", "#");
|
||||
title.textContent = h1.textContent;
|
||||
|
||||
// make the content
|
||||
const content = document.body.querySelector(".td-content");
|
||||
let headings = [].slice.call(content.querySelectorAll("h2, h3, h4, h5, h6, .rendered-data > details > summary > h1"));
|
||||
|
||||
// exclude headings that don't have IDs.
|
||||
headings = headings.filter(heading => heading.id);
|
||||
uniquifyHeadingIds(headings);
|
||||
|
||||
// exclude .rendered-data > h1 headings from the ToC
|
||||
const tocTargets = headings.filter(heading => heading.tagName !== "H1");
|
||||
|
||||
// we have to adjust heading IDs to ensure that they are unique
|
||||
const nav = document.createElement("nav");
|
||||
nav.id = "TableOfContents";
|
||||
|
||||
const section = makeTocSection(tocTargets, 0);
|
||||
nav.appendChild(section.content);
|
||||
// append title and content to the #toc placeholder
|
||||
const toc = document.body.querySelector("#toc");
|
||||
toc.appendChild(title);
|
||||
toc.appendChild(nav);
|
||||
|
||||
// tell ToC items about any rendered-data headings they contain
|
||||
setTocItemChildren(section.content, headings);
|
||||
}
|
||||
|
||||
// create a single ToC entry
|
||||
function makeTocEntry(heading) {
|
||||
const li = document.createElement("li");
|
||||
const a = document.createElement("a");
|
||||
a.setAttribute("href", `#${heading.id}`);
|
||||
a.textContent = heading.textContent;
|
||||
li.appendChild(a);
|
||||
return li;
|
||||
}
|
||||
|
||||
/*
|
||||
Each ToC section is an `<ol>` element.
|
||||
ToC entries are `<li>` elements and these contain nested ToC sections,
|
||||
whenever we go to the next heading level down.
|
||||
*/
|
||||
function makeTocSection(headings, index) {
|
||||
const ol = document.createElement("ol");
|
||||
let previousHeading = null;
|
||||
let previousLi = null;
|
||||
let i = index;
|
||||
const lis = [];
|
||||
|
||||
for (i; i < headings.length; i++) {
|
||||
const thisHeading = headings[i];
|
||||
if (previousHeading && (thisHeading.tagName > previousHeading.tagName)) {
|
||||
// we are going down a heading level, create a new nested section
|
||||
const section = makeTocSection(headings, i);
|
||||
previousLi.appendChild(section.content);
|
||||
i = section.index -1;
|
||||
}
|
||||
else if (previousHeading && (previousHeading.tagName > thisHeading.tagName)) {
|
||||
// we have come back up a level, so a section is finished
|
||||
for (let li of lis) {
|
||||
ol.appendChild(li);
|
||||
}
|
||||
return {
|
||||
content: ol,
|
||||
index: i
|
||||
}
|
||||
}
|
||||
else {
|
||||
// we are still processing this section, so add this heading to the current section
|
||||
previousLi = makeTocEntry(thisHeading);
|
||||
lis.push(previousLi);
|
||||
previousHeading = thisHeading;
|
||||
}
|
||||
}
|
||||
for (let li of lis) {
|
||||
ol.appendChild(li);
|
||||
}
|
||||
return {
|
||||
content: ol,
|
||||
index: i
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Set a new ToC entry.
|
||||
Clear any previously highlighted ToC items, set the new one,
|
||||
and adjust the ToC scroll position.
|
||||
*/
|
||||
function setTocEntry(newEntry) {
|
||||
const activeEntries = document.querySelectorAll("#toc a.active");
|
||||
for (const activeEntry of activeEntries) {
|
||||
activeEntry.classList.remove('active');
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Test whether a node is in the viewport
|
||||
*/
|
||||
function isInViewport(node) {
|
||||
const rect = node.getBoundingClientRect();
|
||||
|
||||
return (
|
||||
rect.top >= 0 &&
|
||||
rect.left >= 0 &&
|
||||
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
|
||||
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
The callback we pass to the IntersectionObserver constructor.
|
||||
|
||||
Called when any of our observed nodes starts or stops intersecting
|
||||
with the viewport.
|
||||
*/
|
||||
function handleIntersectionUpdate(entries) {
|
||||
|
||||
/*
|
||||
Special case: If the current URL hash matches a ToC entry, and
|
||||
the corresponding heading is visible in the viewport, then that is
|
||||
made the current ToC entry, and we don't even look at the intersection
|
||||
observer data.
|
||||
This means that if the user has clicked on a ToC entry,
|
||||
we won't unselect it through the intersection observer.
|
||||
*/
|
||||
const hash = document.location.hash;
|
||||
if (hash) {
|
||||
let tocEntryForHash = document.querySelector(`nav li a[href="${hash}"]`);
|
||||
// if the hash isn't a direct match for a ToC item, check the data attributes
|
||||
if (!tocEntryForHash) {
|
||||
const fragment = hash.substring(1);
|
||||
tocEntryForHash = document.querySelector(`nav li a[data-${fragment}]`);
|
||||
}
|
||||
if (tocEntryForHash) {
|
||||
const headingForHash = document.querySelector(hash);
|
||||
if (headingForHash && isInViewport(headingForHash)) {
|
||||
setTocEntry(tocEntryForHash);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let newEntry = null;
|
||||
|
||||
for (const entry of entries) {
|
||||
if (entry.intersectionRatio > 0) {
|
||||
const heading = entry.target;
|
||||
/*
|
||||
This sidebar nav consists of two sections:
|
||||
* at the top, a sitenav containing links to other pages
|
||||
* under that, the ToC for the current page
|
||||
|
||||
Since the sidebar scrolls to match the document position,
|
||||
the sitenav will tend to scroll off the screen.
|
||||
|
||||
If the user has scrolled up to (or near) the top of the page,
|
||||
we want to show the sitenav so.
|
||||
|
||||
So: if the H1 (title) for the current page has started
|
||||
intersecting, then always scroll the sidebar back to the top.
|
||||
*/
|
||||
if (heading.tagName === "H1" && heading.parentNode.tagName === "DIV") {
|
||||
const nav = document.querySelector("#td-section-nav");
|
||||
nav.scrollTop = 0;
|
||||
return;
|
||||
}
|
||||
/*
|
||||
Otherwise, get the ToC entry for the first entry that
|
||||
entered the viewport, if there was one.
|
||||
*/
|
||||
const id = entry.target.getAttribute('id');
|
||||
let tocEntry = document.querySelector(`nav li a[href="#${id}"]`);
|
||||
// if the id isn't a direct match for a ToC item,
|
||||
// check the ToC entry's `data-*` attributes
|
||||
if (!tocEntry) {
|
||||
tocEntry = document.querySelector(`nav li a[data-${id}]`);
|
||||
}
|
||||
if (tocEntry && !newEntry) {
|
||||
newEntry = tocEntry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (newEntry) {
|
||||
setTocEntry(newEntry);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Track when headings enter the viewport, and use this to update the highlight
|
||||
for the corresponding ToC entry.
|
||||
*/
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
makeToc();
|
||||
|
||||
const toc = document.querySelector("#toc");
|
||||
toc.addEventListener("click", event => {
|
||||
if (event.target.tagName === "A") {
|
||||
setTocEntry(event.target);
|
||||
}
|
||||
});
|
||||
|
||||
const observer = new IntersectionObserver(handleIntersectionUpdate);
|
||||
|
||||
document.querySelectorAll("h1, h2, h3, h4, h5, h6").forEach((section) => {
|
||||
observer.observe(section);
|
||||
});
|
||||
|
||||
});
|
@ -0,0 +1 @@
|
||||
Subproject commit b7562e9cb99c652ebb5ade75d2203db08ad81ede
|
Loading…
Reference in New Issue