reffy 6.2.0 → 6.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +158 -158
- package/index.js +11 -11
- package/package.json +53 -53
- package/reffy.js +248 -248
- package/src/browserlib/canonicalize-url.mjs +50 -50
- package/src/browserlib/create-outline.mjs +352 -352
- package/src/browserlib/extract-cssdfn.mjs +319 -319
- package/src/browserlib/extract-dfns.mjs +686 -686
- package/src/browserlib/extract-elements.mjs +205 -205
- package/src/browserlib/extract-headings.mjs +48 -48
- package/src/browserlib/extract-ids.mjs +28 -28
- package/src/browserlib/extract-links.mjs +28 -28
- package/src/browserlib/extract-references.mjs +203 -203
- package/src/browserlib/extract-webidl.mjs +134 -134
- package/src/browserlib/get-absolute-url.mjs +21 -21
- package/src/browserlib/get-generator.mjs +26 -26
- package/src/browserlib/get-lastmodified-date.mjs +13 -13
- package/src/browserlib/get-title.mjs +11 -11
- package/src/browserlib/informative-selector.mjs +16 -16
- package/src/browserlib/map-ids-to-headings.mjs +136 -136
- package/src/browserlib/reffy.json +53 -53
- package/src/cli/check-missing-dfns.js +609 -609
- package/src/cli/generate-idlnames.js +430 -430
- package/src/cli/generate-idlparsed.js +139 -139
- package/src/cli/merge-crawl-results.js +128 -128
- package/src/cli/parse-webidl.js +430 -430
- package/src/lib/css-grammar-parse-tree.schema.json +109 -109
- package/src/lib/css-grammar-parser.js +440 -440
- package/src/lib/fetch.js +55 -55
- package/src/lib/nock-server.js +119 -119
- package/src/lib/specs-crawler.js +605 -603
- package/src/lib/util.js +898 -898
- package/src/specs/missing-css-rules.json +197 -197
- package/src/specs/spec-equivalents.json +149 -149
- package/src/browserlib/extract-editors.mjs~ +0 -14
- package/src/browserlib/generate-es-dfn-report.sh~ +0 -4
- package/src/cli/csstree-grammar-check.js +0 -28
- package/src/cli/csstree-grammar-check.js~ +0 -10
- package/src/cli/csstree-grammar-parser.js +0 -11
- package/src/cli/csstree-grammar-parser.js~ +0 -1
- package/src/cli/extract-editors.js~ +0 -38
- package/src/cli/process-specs.js~ +0 -28
|
@@ -1,205 +1,205 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Extract the list of markup elements that the spec defines
|
|
3
|
-
*
|
|
4
|
-
* Extraction requires spec to follow a structure similar to the one used in
|
|
5
|
-
* the HTML spec:
|
|
6
|
-
* https://html.spec.whatwg.org/multipage/dom.html#element-definitions
|
|
7
|
-
*
|
|
8
|
-
* Important (2021-05-31): Extraction code is incomplete and only extracts the
|
|
9
|
-
* mapping between each element and its associated IDL interface for now. The
|
|
10
|
-
* rest of the extraction logic is currently disabled because it does not yet
|
|
11
|
-
* produce fully machine-readable results, see inline comments prefixed with
|
|
12
|
-
* "TODO" for details.
|
|
13
|
-
*
|
|
14
|
-
* @function
|
|
15
|
-
* @public
|
|
16
|
-
*/
|
|
17
|
-
export default function () {
|
|
18
|
-
function getText(el) {
|
|
19
|
-
return el.textContent.trim().replace(/\s+/g, ' ');
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// Extract HTML elements
|
|
23
|
-
const htmlElements = [...document.querySelectorAll('dl.element')]
|
|
24
|
-
.map(el => {
|
|
25
|
-
// Get back to heading that defines the element(s)
|
|
26
|
-
let heading = el.previousElementSibling;
|
|
27
|
-
while (heading && !heading.nodeName.match(/^H\d$/)) {
|
|
28
|
-
heading = heading.previousElementSibling;
|
|
29
|
-
}
|
|
30
|
-
if (!heading) {
|
|
31
|
-
throw new Error('Could not locate heading associated with element');
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const dfns = [...heading.querySelectorAll('dfn')];
|
|
35
|
-
if (dfns.length === 0) {
|
|
36
|
-
// Ignore the definition of "Custom elements" in HTML
|
|
37
|
-
if (getText(heading).match(/Core concepts/)) {
|
|
38
|
-
return null;
|
|
39
|
-
}
|
|
40
|
-
throw new Error('No dfn found in heading element: ' + heading.textContent);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// In most cases, there will be only one element, but some elements are
|
|
44
|
-
// defined together, typically h1-h6 or sub and sup
|
|
45
|
-
return dfns.map(dfn => {
|
|
46
|
-
const res = { name: getText(dfn) };
|
|
47
|
-
const dts = [...el.querySelectorAll('dt')];
|
|
48
|
-
dts.forEach(dt => {
|
|
49
|
-
const prop = ({
|
|
50
|
-
// TODO: Properties other than "interface" follow common patterns
|
|
51
|
-
// most of the time... but not always, making it hard to create
|
|
52
|
-
// fully machine-readable info. Uncomment following lines to extract
|
|
53
|
-
// more info and improve the code
|
|
54
|
-
// 'Categories': 'categories',
|
|
55
|
-
// 'Contexts in which this element can be used': 'contexts',
|
|
56
|
-
// 'Content model': 'contents',
|
|
57
|
-
// 'Tag omission in text/html': 'omission',
|
|
58
|
-
// 'Content attributes': 'attributes',
|
|
59
|
-
// 'Accessibility considerations': 'accessibility',
|
|
60
|
-
'DOM interface': 'interface'
|
|
61
|
-
})[getText(dt).replace(/:$/, '')];
|
|
62
|
-
|
|
63
|
-
if (prop === 'omission') {
|
|
64
|
-
// Tag omission can be a complex paragraph but it is never a list.
|
|
65
|
-
let dd = dt.nextElementSibling;
|
|
66
|
-
while (dd && dd.nodeName !== 'DD') {
|
|
67
|
-
dd = dd.nextElementSibling;
|
|
68
|
-
}
|
|
69
|
-
if (dd) {
|
|
70
|
-
res[prop] = getText(dd);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
else if (prop === 'accessibility') {
|
|
74
|
-
// TODO: Accessibility considerations links to ARIA and AAM. The
|
|
75
|
-
// code does not yet handle more complex associations that currently
|
|
76
|
-
// appear in the definitions of the img, sub and sup, input and
|
|
77
|
-
// select elements.
|
|
78
|
-
res[prop] = [];
|
|
79
|
-
let dd = dt.nextElementSibling;
|
|
80
|
-
while (dd && dd.nodeName !== 'DT') {
|
|
81
|
-
if (dd.nodeName === 'DD') {
|
|
82
|
-
const link = dd.querySelector('a');
|
|
83
|
-
if (link) {
|
|
84
|
-
res[prop].push({
|
|
85
|
-
title: getText(link),
|
|
86
|
-
href: link.getAttribute('href')
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
dd = dd.nextElementSibling;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
else if (prop === 'interface') {
|
|
94
|
-
let dd = dt.nextElementSibling;
|
|
95
|
-
while (dd && dd.nodeName !== 'DD') {
|
|
96
|
-
dd = dd.nextElementSibling;
|
|
97
|
-
}
|
|
98
|
-
if (dd) {
|
|
99
|
-
let match = dd.textContent.match(/^interface (.*?) /m);
|
|
100
|
-
if (match) {
|
|
101
|
-
res[prop] = match[1];
|
|
102
|
-
}
|
|
103
|
-
else {
|
|
104
|
-
// NB: The sub/sup table uses a plural form for "Use", hence
|
|
105
|
-
// the "?" for the final "s" in "Uses".
|
|
106
|
-
match = dd.textContent.match(/^Uses? (.*?)[,\.\s]/);
|
|
107
|
-
if (match) {
|
|
108
|
-
res[prop] = match[1];
|
|
109
|
-
}
|
|
110
|
-
else {
|
|
111
|
-
throw new Error('Could not link element to interface: ' + getText(dd));
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
else {
|
|
116
|
-
throw new Error('Could not link element to interface, missing dd for ' + res.name);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
else if (prop) {
|
|
120
|
-
res[prop] = [];
|
|
121
|
-
let dd = dt.nextElementSibling;
|
|
122
|
-
while (dd && dd.nodeName !== 'DT') {
|
|
123
|
-
if (dd.nodeName === 'DD') {
|
|
124
|
-
res[prop].push(getText(dd).replace(/\.$/, ''));
|
|
125
|
-
}
|
|
126
|
-
dd = dd.nextElementSibling;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
return res;
|
|
131
|
-
});
|
|
132
|
-
})
|
|
133
|
-
.filter(el => !!el)
|
|
134
|
-
.flat();
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
// Extract SVG elements that use the "element-summary" pattern
|
|
138
|
-
const svgSummaryElements = [...document.querySelectorAll('div.element-summary')]
|
|
139
|
-
.map(el => {
|
|
140
|
-
const name = el.querySelector('.element-name');
|
|
141
|
-
if (!name) {
|
|
142
|
-
throw new Error('Could not extract name from element-summary element');
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
const res = { name: getText(name).replace(/‘|’/g, '') };
|
|
146
|
-
const dts = [...el.querySelectorAll('dt')];
|
|
147
|
-
dts.forEach(dt => {
|
|
148
|
-
const prop = ({
|
|
149
|
-
'DOM Interfaces': 'interface'
|
|
150
|
-
})[getText(dt).replace(/:$/, '')];
|
|
151
|
-
|
|
152
|
-
if (prop === 'interface') {
|
|
153
|
-
let dd = dt.nextElementSibling;
|
|
154
|
-
while (dd && dd.nodeName !== 'DD') {
|
|
155
|
-
dd = dd.nextElementSibling;
|
|
156
|
-
}
|
|
157
|
-
if (dd) {
|
|
158
|
-
// For some reason, the "discard" element has no interface:
|
|
159
|
-
// https://svgwg.org/specs/animations/#DiscardElement
|
|
160
|
-
if (getText(dd)) {
|
|
161
|
-
res[prop] = getText(dd);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
else {
|
|
165
|
-
throw new Error('Could not link element to interface, missing dd for ' + res.name);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
});
|
|
169
|
-
return res;
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
// Extract SVG elements that use the "definition-table" pattern
|
|
174
|
-
const svgTableElements = [...document.querySelectorAll('table.definition-table')]
|
|
175
|
-
.map(el => {
|
|
176
|
-
const dfn = el.querySelector('dfn');
|
|
177
|
-
if (!dfn) {
|
|
178
|
-
throw new Error('Could not extract name from definition-table element');
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const res = { name: getText(dfn) };
|
|
182
|
-
const ths = [...el.querySelectorAll('th')];
|
|
183
|
-
ths.forEach(th => {
|
|
184
|
-
const prop = ({
|
|
185
|
-
'DOM Interfaces': 'interface'
|
|
186
|
-
})[getText(th).replace(/:$/, '')];
|
|
187
|
-
|
|
188
|
-
if (prop === 'interface') {
|
|
189
|
-
let td = th.nextElementSibling;
|
|
190
|
-
while (td && td.nodeName !== 'TD') {
|
|
191
|
-
td = td.nextElementSibling;
|
|
192
|
-
}
|
|
193
|
-
if (td) {
|
|
194
|
-
res[prop] = getText(td);
|
|
195
|
-
}
|
|
196
|
-
else {
|
|
197
|
-
throw new Error('Could not link element to interface, missing cell for ' + res.name);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
});
|
|
201
|
-
return res;
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
return htmlElements.concat(svgSummaryElements, svgTableElements);
|
|
205
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Extract the list of markup elements that the spec defines
|
|
3
|
+
*
|
|
4
|
+
* Extraction requires spec to follow a structure similar to the one used in
|
|
5
|
+
* the HTML spec:
|
|
6
|
+
* https://html.spec.whatwg.org/multipage/dom.html#element-definitions
|
|
7
|
+
*
|
|
8
|
+
* Important (2021-05-31): Extraction code is incomplete and only extracts the
|
|
9
|
+
* mapping between each element and its associated IDL interface for now. The
|
|
10
|
+
* rest of the extraction logic is currently disabled because it does not yet
|
|
11
|
+
* produce fully machine-readable results, see inline comments prefixed with
|
|
12
|
+
* "TODO" for details.
|
|
13
|
+
*
|
|
14
|
+
* @function
|
|
15
|
+
* @public
|
|
16
|
+
*/
|
|
17
|
+
export default function () {
|
|
18
|
+
function getText(el) {
|
|
19
|
+
return el.textContent.trim().replace(/\s+/g, ' ');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Extract HTML elements
|
|
23
|
+
const htmlElements = [...document.querySelectorAll('dl.element')]
|
|
24
|
+
.map(el => {
|
|
25
|
+
// Get back to heading that defines the element(s)
|
|
26
|
+
let heading = el.previousElementSibling;
|
|
27
|
+
while (heading && !heading.nodeName.match(/^H\d$/)) {
|
|
28
|
+
heading = heading.previousElementSibling;
|
|
29
|
+
}
|
|
30
|
+
if (!heading) {
|
|
31
|
+
throw new Error('Could not locate heading associated with element');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const dfns = [...heading.querySelectorAll('dfn')];
|
|
35
|
+
if (dfns.length === 0) {
|
|
36
|
+
// Ignore the definition of "Custom elements" in HTML
|
|
37
|
+
if (getText(heading).match(/Core concepts/)) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
throw new Error('No dfn found in heading element: ' + heading.textContent);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// In most cases, there will be only one element, but some elements are
|
|
44
|
+
// defined together, typically h1-h6 or sub and sup
|
|
45
|
+
return dfns.map(dfn => {
|
|
46
|
+
const res = { name: getText(dfn) };
|
|
47
|
+
const dts = [...el.querySelectorAll('dt')];
|
|
48
|
+
dts.forEach(dt => {
|
|
49
|
+
const prop = ({
|
|
50
|
+
// TODO: Properties other than "interface" follow common patterns
|
|
51
|
+
// most of the time... but not always, making it hard to create
|
|
52
|
+
// fully machine-readable info. Uncomment following lines to extract
|
|
53
|
+
// more info and improve the code
|
|
54
|
+
// 'Categories': 'categories',
|
|
55
|
+
// 'Contexts in which this element can be used': 'contexts',
|
|
56
|
+
// 'Content model': 'contents',
|
|
57
|
+
// 'Tag omission in text/html': 'omission',
|
|
58
|
+
// 'Content attributes': 'attributes',
|
|
59
|
+
// 'Accessibility considerations': 'accessibility',
|
|
60
|
+
'DOM interface': 'interface'
|
|
61
|
+
})[getText(dt).replace(/:$/, '')];
|
|
62
|
+
|
|
63
|
+
if (prop === 'omission') {
|
|
64
|
+
// Tag omission can be a complex paragraph but it is never a list.
|
|
65
|
+
let dd = dt.nextElementSibling;
|
|
66
|
+
while (dd && dd.nodeName !== 'DD') {
|
|
67
|
+
dd = dd.nextElementSibling;
|
|
68
|
+
}
|
|
69
|
+
if (dd) {
|
|
70
|
+
res[prop] = getText(dd);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
else if (prop === 'accessibility') {
|
|
74
|
+
// TODO: Accessibility considerations links to ARIA and AAM. The
|
|
75
|
+
// code does not yet handle more complex associations that currently
|
|
76
|
+
// appear in the definitions of the img, sub and sup, input and
|
|
77
|
+
// select elements.
|
|
78
|
+
res[prop] = [];
|
|
79
|
+
let dd = dt.nextElementSibling;
|
|
80
|
+
while (dd && dd.nodeName !== 'DT') {
|
|
81
|
+
if (dd.nodeName === 'DD') {
|
|
82
|
+
const link = dd.querySelector('a');
|
|
83
|
+
if (link) {
|
|
84
|
+
res[prop].push({
|
|
85
|
+
title: getText(link),
|
|
86
|
+
href: link.getAttribute('href')
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
dd = dd.nextElementSibling;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
else if (prop === 'interface') {
|
|
94
|
+
let dd = dt.nextElementSibling;
|
|
95
|
+
while (dd && dd.nodeName !== 'DD') {
|
|
96
|
+
dd = dd.nextElementSibling;
|
|
97
|
+
}
|
|
98
|
+
if (dd) {
|
|
99
|
+
let match = dd.textContent.match(/^interface (.*?) /m);
|
|
100
|
+
if (match) {
|
|
101
|
+
res[prop] = match[1];
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
// NB: The sub/sup table uses a plural form for "Use", hence
|
|
105
|
+
// the "?" for the final "s" in "Uses".
|
|
106
|
+
match = dd.textContent.match(/^Uses? (.*?)[,\.\s]/);
|
|
107
|
+
if (match) {
|
|
108
|
+
res[prop] = match[1];
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
throw new Error('Could not link element to interface: ' + getText(dd));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
throw new Error('Could not link element to interface, missing dd for ' + res.name);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
else if (prop) {
|
|
120
|
+
res[prop] = [];
|
|
121
|
+
let dd = dt.nextElementSibling;
|
|
122
|
+
while (dd && dd.nodeName !== 'DT') {
|
|
123
|
+
if (dd.nodeName === 'DD') {
|
|
124
|
+
res[prop].push(getText(dd).replace(/\.$/, ''));
|
|
125
|
+
}
|
|
126
|
+
dd = dd.nextElementSibling;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
return res;
|
|
131
|
+
});
|
|
132
|
+
})
|
|
133
|
+
.filter(el => !!el)
|
|
134
|
+
.flat();
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
// Extract SVG elements that use the "element-summary" pattern
|
|
138
|
+
const svgSummaryElements = [...document.querySelectorAll('div.element-summary')]
|
|
139
|
+
.map(el => {
|
|
140
|
+
const name = el.querySelector('.element-name');
|
|
141
|
+
if (!name) {
|
|
142
|
+
throw new Error('Could not extract name from element-summary element');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const res = { name: getText(name).replace(/‘|’/g, '') };
|
|
146
|
+
const dts = [...el.querySelectorAll('dt')];
|
|
147
|
+
dts.forEach(dt => {
|
|
148
|
+
const prop = ({
|
|
149
|
+
'DOM Interfaces': 'interface'
|
|
150
|
+
})[getText(dt).replace(/:$/, '')];
|
|
151
|
+
|
|
152
|
+
if (prop === 'interface') {
|
|
153
|
+
let dd = dt.nextElementSibling;
|
|
154
|
+
while (dd && dd.nodeName !== 'DD') {
|
|
155
|
+
dd = dd.nextElementSibling;
|
|
156
|
+
}
|
|
157
|
+
if (dd) {
|
|
158
|
+
// For some reason, the "discard" element has no interface:
|
|
159
|
+
// https://svgwg.org/specs/animations/#DiscardElement
|
|
160
|
+
if (getText(dd)) {
|
|
161
|
+
res[prop] = getText(dd);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
throw new Error('Could not link element to interface, missing dd for ' + res.name);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
return res;
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
// Extract SVG elements that use the "definition-table" pattern
|
|
174
|
+
const svgTableElements = [...document.querySelectorAll('table.definition-table')]
|
|
175
|
+
.map(el => {
|
|
176
|
+
const dfn = el.querySelector('dfn');
|
|
177
|
+
if (!dfn) {
|
|
178
|
+
throw new Error('Could not extract name from definition-table element');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const res = { name: getText(dfn) };
|
|
182
|
+
const ths = [...el.querySelectorAll('th')];
|
|
183
|
+
ths.forEach(th => {
|
|
184
|
+
const prop = ({
|
|
185
|
+
'DOM Interfaces': 'interface'
|
|
186
|
+
})[getText(th).replace(/:$/, '')];
|
|
187
|
+
|
|
188
|
+
if (prop === 'interface') {
|
|
189
|
+
let td = th.nextElementSibling;
|
|
190
|
+
while (td && td.nodeName !== 'TD') {
|
|
191
|
+
td = td.nextElementSibling;
|
|
192
|
+
}
|
|
193
|
+
if (td) {
|
|
194
|
+
res[prop] = getText(td);
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
throw new Error('Could not link element to interface, missing cell for ' + res.name);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
return res;
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
return htmlElements.concat(svgSummaryElements, svgTableElements);
|
|
205
|
+
}
|
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
import getAbsoluteUrl from './get-absolute-url.mjs';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Extract headings data from documents
|
|
5
|
-
*/
|
|
6
|
-
export default function (spec, idToHeading) {
|
|
7
|
-
// Compute once whether we created a single page version out of multiple pages
|
|
8
|
-
const singlePage = !document.querySelector('[data-reffy-page]');
|
|
9
|
-
|
|
10
|
-
// Headings using the markup convention of the EcmaScript spec
|
|
11
|
-
const esHeadings = [...document.querySelectorAll('emu-clause[id] > h1')].map(n => {
|
|
12
|
-
const headingNumber = n.querySelector(".secnum")?.textContent;
|
|
13
|
-
const headingLevel = headingNumber ? headingNumber.split(".").length : undefined;
|
|
14
|
-
return {
|
|
15
|
-
id: n.parentNode.id,
|
|
16
|
-
title: n.textContent.replace(headingNumber, '').trim(),
|
|
17
|
-
level: headingLevel,
|
|
18
|
-
number: headingNumber
|
|
19
|
-
};
|
|
20
|
-
});
|
|
21
|
-
return esHeadings.concat([...document.querySelectorAll('h1[id], h2[id], h3[id], h4[id], h5[id] ,h6[id]')].map(n => {
|
|
22
|
-
// Note: In theory, all <hX> heading elements that have an ID are associated
|
|
23
|
-
// with a heading in idToHeading. One exception to the rule: when the
|
|
24
|
-
// heading element appears in a <hgroup> element, the mapping is not
|
|
25
|
-
// properly done (the outline creation algorithm explicitly skips these
|
|
26
|
-
// headings not to create a mess in the outline). In practice, this only
|
|
27
|
-
// really happens so far for WHATWG spec titles that (correctly) group the
|
|
28
|
-
// title and subtitle headings in a <hgroup>.
|
|
29
|
-
const href = getAbsoluteUrl(n, { singlePage });
|
|
30
|
-
const heading = idToHeading[href] || {
|
|
31
|
-
id: n.id,
|
|
32
|
-
href,
|
|
33
|
-
title: n.textContent.trim()
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const res = {
|
|
37
|
-
id: heading.id,
|
|
38
|
-
href: heading.href,
|
|
39
|
-
level: parseInt(n.tagName.slice(1), 10),
|
|
40
|
-
title: heading.title
|
|
41
|
-
};
|
|
42
|
-
if (heading.number) {
|
|
43
|
-
res.number = heading.number;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return res;
|
|
47
|
-
}));
|
|
48
|
-
}
|
|
1
|
+
import getAbsoluteUrl from './get-absolute-url.mjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Extract headings data from documents
|
|
5
|
+
*/
|
|
6
|
+
export default function (spec, idToHeading) {
|
|
7
|
+
// Compute once whether we created a single page version out of multiple pages
|
|
8
|
+
const singlePage = !document.querySelector('[data-reffy-page]');
|
|
9
|
+
|
|
10
|
+
// Headings using the markup convention of the EcmaScript spec
|
|
11
|
+
const esHeadings = [...document.querySelectorAll('emu-clause[id] > h1')].map(n => {
|
|
12
|
+
const headingNumber = n.querySelector(".secnum")?.textContent;
|
|
13
|
+
const headingLevel = headingNumber ? headingNumber.split(".").length : undefined;
|
|
14
|
+
return {
|
|
15
|
+
id: n.parentNode.id,
|
|
16
|
+
title: n.textContent.replace(headingNumber, '').trim(),
|
|
17
|
+
level: headingLevel,
|
|
18
|
+
number: headingNumber
|
|
19
|
+
};
|
|
20
|
+
});
|
|
21
|
+
return esHeadings.concat([...document.querySelectorAll('h1[id], h2[id], h3[id], h4[id], h5[id] ,h6[id]')].map(n => {
|
|
22
|
+
// Note: In theory, all <hX> heading elements that have an ID are associated
|
|
23
|
+
// with a heading in idToHeading. One exception to the rule: when the
|
|
24
|
+
// heading element appears in a <hgroup> element, the mapping is not
|
|
25
|
+
// properly done (the outline creation algorithm explicitly skips these
|
|
26
|
+
// headings not to create a mess in the outline). In practice, this only
|
|
27
|
+
// really happens so far for WHATWG spec titles that (correctly) group the
|
|
28
|
+
// title and subtitle headings in a <hgroup>.
|
|
29
|
+
const href = getAbsoluteUrl(n, { singlePage });
|
|
30
|
+
const heading = idToHeading[href] || {
|
|
31
|
+
id: n.id,
|
|
32
|
+
href,
|
|
33
|
+
title: n.textContent.trim()
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const res = {
|
|
37
|
+
id: heading.id,
|
|
38
|
+
href: heading.href,
|
|
39
|
+
level: parseInt(n.tagName.slice(1), 10),
|
|
40
|
+
title: heading.title
|
|
41
|
+
};
|
|
42
|
+
if (heading.number) {
|
|
43
|
+
res.number = heading.number;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return res;
|
|
47
|
+
}));
|
|
48
|
+
}
|
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
import getAbsoluteUrl from './get-absolute-url.mjs';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Extract absolute ids from documents
|
|
5
|
-
*/
|
|
6
|
-
export default function () {
|
|
7
|
-
// Compute once whether we created a single page version out of multiple pages
|
|
8
|
-
const singlePage = !document.querySelector('[data-reffy-page]');
|
|
9
|
-
|
|
10
|
-
return [...document.querySelectorAll('*[id]')]
|
|
11
|
-
// Ignore respec- prefixed ids to avoid keeping track of their evolution
|
|
12
|
-
// They're clearly not meant to be link target in any case
|
|
13
|
-
.filter(n => !n.id.startsWith('respec-'))
|
|
14
|
-
|
|
15
|
-
// Ignore dfn-panel- prefixed ids that ReSpec generates to avoid keeping
|
|
16
|
-
// track of their evolution. They're clearly not meant to be link target
|
|
17
|
-
// either.
|
|
18
|
-
.filter(n => !n.id.startsWith('dfn-panel-'))
|
|
19
|
-
|
|
20
|
-
// Convert IDs to absolute URLs (needed to handle multipage specs)
|
|
21
|
-
.map(n => getAbsoluteUrl(n, { singlePage }))
|
|
22
|
-
|
|
23
|
-
// Capture anchors set in <a name> when they're not dup of ids
|
|
24
|
-
.concat([...document.querySelectorAll('a[name]')]
|
|
25
|
-
.filter(n => !n.id || n.id !== n.name)
|
|
26
|
-
.map(n => getAbsoluteUrl(n, { singlePage, attribute: 'name' }))
|
|
27
|
-
);
|
|
28
|
-
}
|
|
1
|
+
import getAbsoluteUrl from './get-absolute-url.mjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Extract absolute ids from documents
|
|
5
|
+
*/
|
|
6
|
+
export default function () {
|
|
7
|
+
// Compute once whether we created a single page version out of multiple pages
|
|
8
|
+
const singlePage = !document.querySelector('[data-reffy-page]');
|
|
9
|
+
|
|
10
|
+
return [...document.querySelectorAll('*[id]')]
|
|
11
|
+
// Ignore respec- prefixed ids to avoid keeping track of their evolution
|
|
12
|
+
// They're clearly not meant to be link target in any case
|
|
13
|
+
.filter(n => !n.id.startsWith('respec-'))
|
|
14
|
+
|
|
15
|
+
// Ignore dfn-panel- prefixed ids that ReSpec generates to avoid keeping
|
|
16
|
+
// track of their evolution. They're clearly not meant to be link target
|
|
17
|
+
// either.
|
|
18
|
+
.filter(n => !n.id.startsWith('dfn-panel-'))
|
|
19
|
+
|
|
20
|
+
// Convert IDs to absolute URLs (needed to handle multipage specs)
|
|
21
|
+
.map(n => getAbsoluteUrl(n, { singlePage }))
|
|
22
|
+
|
|
23
|
+
// Capture anchors set in <a name> when they're not dup of ids
|
|
24
|
+
.concat([...document.querySelectorAll('a[name]')]
|
|
25
|
+
.filter(n => !n.id || n.id !== n.name)
|
|
26
|
+
.map(n => getAbsoluteUrl(n, { singlePage, attribute: 'name' }))
|
|
27
|
+
);
|
|
28
|
+
}
|
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
import { canonicalizeUrl } from './canonicalize-url.mjs';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Extract and canonicalize absolute links of the document and their fragments
|
|
5
|
-
* FIXME: ⚠ Modify the DOM
|
|
6
|
-
*/
|
|
7
|
-
export default function () {
|
|
8
|
-
// Ignore links from the "head" section, which either link to
|
|
9
|
-
// self, the GitHub repo, the implementation report, and other
|
|
10
|
-
// documents that don't need to appear in the list of references.
|
|
11
|
-
const links = {};
|
|
12
|
-
[...document.querySelectorAll('.head a[href]')].forEach(n => n.href = '');
|
|
13
|
-
document.querySelectorAll('a[href^=http]').forEach(n => {
|
|
14
|
-
const url = canonicalizeUrl(n.href);
|
|
15
|
-
if (!links[url]) {
|
|
16
|
-
links[url] = new Set();
|
|
17
|
-
}
|
|
18
|
-
if (n.href.includes('#') && n.href.split('#')[1]) {
|
|
19
|
-
links[url].add(n.href.split('#')[1]);
|
|
20
|
-
}
|
|
21
|
-
});
|
|
22
|
-
return Object.keys(links)
|
|
23
|
-
// turning sets into arrays
|
|
24
|
-
.reduce((acc, u) => {
|
|
25
|
-
acc[u] = [...links[u]];
|
|
26
|
-
return acc;
|
|
27
|
-
}, {});
|
|
28
|
-
}
|
|
1
|
+
import { canonicalizeUrl } from './canonicalize-url.mjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Extract and canonicalize absolute links of the document and their fragments
|
|
5
|
+
* FIXME: ⚠ Modify the DOM
|
|
6
|
+
*/
|
|
7
|
+
export default function () {
|
|
8
|
+
// Ignore links from the "head" section, which either link to
|
|
9
|
+
// self, the GitHub repo, the implementation report, and other
|
|
10
|
+
// documents that don't need to appear in the list of references.
|
|
11
|
+
const links = {};
|
|
12
|
+
[...document.querySelectorAll('.head a[href]')].forEach(n => n.href = '');
|
|
13
|
+
document.querySelectorAll('a[href^=http]').forEach(n => {
|
|
14
|
+
const url = canonicalizeUrl(n.href);
|
|
15
|
+
if (!links[url]) {
|
|
16
|
+
links[url] = new Set();
|
|
17
|
+
}
|
|
18
|
+
if (n.href.includes('#') && n.href.split('#')[1]) {
|
|
19
|
+
links[url].add(n.href.split('#')[1]);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
return Object.keys(links)
|
|
23
|
+
// turning sets into arrays
|
|
24
|
+
.reduce((acc, u) => {
|
|
25
|
+
acc[u] = [...links[u]];
|
|
26
|
+
return acc;
|
|
27
|
+
}, {});
|
|
28
|
+
}
|