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,686 +1,686 @@
|
|
|
1
|
-
import extractWebIdl from './extract-webidl.mjs';
|
|
2
|
-
import informativeSelector from './informative-selector.mjs';
|
|
3
|
-
import {parse} from "../../node_modules/webidl2/index.js";
|
|
4
|
-
/**
|
|
5
|
-
* Extract definitions in the spec that follow the "Definitions data model":
|
|
6
|
-
* https://tabatkins.github.io/bikeshed/#dfn-contract
|
|
7
|
-
*
|
|
8
|
-
* Each definition returned by the function will have the following properties:
|
|
9
|
-
* - id: The local ID in the DOM. Should be unique within a spec page.
|
|
10
|
-
* - href: The absolute URL to the definition.
|
|
11
|
-
* - linkingText: List of linking phrases for references.
|
|
12
|
-
* - localLinkingText: List of linking phrases for local references only.
|
|
13
|
-
* - type: The definition type. One of the values in
|
|
14
|
-
* https://tabatkins.github.io/bikeshed/#dfn-types
|
|
15
|
-
* - for: The list of namespaces for the definition
|
|
16
|
-
* - access: "public" when definition can be referenced by other specifications,
|
|
17
|
-
* "private" when it should be viewed as a local definition.
|
|
18
|
-
* - informative: true when definition appears in an informative section,
|
|
19
|
-
* false if it is normative
|
|
20
|
-
* - heading: Heading under which the term is to be found. An object with "id",
|
|
21
|
-
* "title", and "number" properties
|
|
22
|
-
* - definedIn: An indication of where the definition appears in the spec. Value
|
|
23
|
-
* can be one of "dt", "pre", "table", "heading", "note", "example", or
|
|
24
|
-
* "prose" (last one indicates that definition appears in the main body of
|
|
25
|
-
* the spec)
|
|
26
|
-
*
|
|
27
|
-
* @function
|
|
28
|
-
* @public
|
|
29
|
-
* @return {Array(Object)} An Array of definitions
|
|
30
|
-
*/
|
|
31
|
-
|
|
32
|
-
function normalize(str) {
|
|
33
|
-
return str.trim().replace(/\s+/g, ' ');
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Valid types defined in https://tabatkins.github.io/bikeshed/#dfn-types
|
|
37
|
-
// (+ "namespace" and "event" which are not yet in the doc)
|
|
38
|
-
function hasValidType(el) {
|
|
39
|
-
const validDfnTypes = [
|
|
40
|
-
// CSS types
|
|
41
|
-
'property',
|
|
42
|
-
'descriptor',
|
|
43
|
-
'value',
|
|
44
|
-
'type',
|
|
45
|
-
'at-rule',
|
|
46
|
-
'function',
|
|
47
|
-
'selector',
|
|
48
|
-
|
|
49
|
-
// Web IDL types
|
|
50
|
-
'namespace',
|
|
51
|
-
'interface',
|
|
52
|
-
'constructor',
|
|
53
|
-
'method',
|
|
54
|
-
'argument',
|
|
55
|
-
'attribute',
|
|
56
|
-
'callback',
|
|
57
|
-
'dictionary',
|
|
58
|
-
'dict-member',
|
|
59
|
-
'enum',
|
|
60
|
-
'enum-value',
|
|
61
|
-
'exception',
|
|
62
|
-
'const',
|
|
63
|
-
'typedef',
|
|
64
|
-
'stringifier',
|
|
65
|
-
'serializer',
|
|
66
|
-
'iterator',
|
|
67
|
-
'maplike',
|
|
68
|
-
'setlike',
|
|
69
|
-
'extended-attribute',
|
|
70
|
-
'event',
|
|
71
|
-
|
|
72
|
-
// Element types
|
|
73
|
-
'element',
|
|
74
|
-
'element-state',
|
|
75
|
-
'element-attr',
|
|
76
|
-
'attr-value',
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
// URL scheme
|
|
80
|
-
'scheme',
|
|
81
|
-
|
|
82
|
-
// HTTP header
|
|
83
|
-
'http-header',
|
|
84
|
-
|
|
85
|
-
// Grammar type
|
|
86
|
-
'grammar',
|
|
87
|
-
|
|
88
|
-
// "English" terms
|
|
89
|
-
'abstract-op',
|
|
90
|
-
'dfn'
|
|
91
|
-
];
|
|
92
|
-
|
|
93
|
-
const type = el.getAttribute('data-dfn-type') ?? 'dfn';
|
|
94
|
-
const isValid = validDfnTypes.includes(type);
|
|
95
|
-
if (!isValid) {
|
|
96
|
-
console.warn('[reffy]', `"${type}" is an invalid dfn type for "${normalize(el.textContent)}"`);
|
|
97
|
-
}
|
|
98
|
-
return isValid;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
function definitionMapper(el, idToHeading) {
|
|
103
|
-
let definedIn = 'prose';
|
|
104
|
-
const enclosingEl = el.closest('dt,pre,table,h1,h2,h3,h4,h5,h6,.note,.example') || el;
|
|
105
|
-
switch (enclosingEl.nodeName) {
|
|
106
|
-
case 'DT':
|
|
107
|
-
case 'PRE':
|
|
108
|
-
case 'TABLE':
|
|
109
|
-
definedIn = enclosingEl.nodeName.toLowerCase();
|
|
110
|
-
break;
|
|
111
|
-
case 'H1':
|
|
112
|
-
case 'H2':
|
|
113
|
-
case 'H3':
|
|
114
|
-
case 'H4':
|
|
115
|
-
case 'H5':
|
|
116
|
-
case 'H6':
|
|
117
|
-
definedIn = 'heading';
|
|
118
|
-
break;
|
|
119
|
-
default:
|
|
120
|
-
if (enclosingEl.classList.contains('note')) {
|
|
121
|
-
definedIn = 'note';
|
|
122
|
-
}
|
|
123
|
-
else if (enclosingEl.classList.contains('example')) {
|
|
124
|
-
definedIn = 'example';
|
|
125
|
-
}
|
|
126
|
-
break;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Compute the absolute URL with fragment
|
|
130
|
-
// (Note the crawler merges pages of a multi-page spec in the first page
|
|
131
|
-
// to ease parsing logic, and we want to get back to the URL of the page)
|
|
132
|
-
const page = el.closest('[data-reffy-page]')?.getAttribute('data-reffy-page');
|
|
133
|
-
const url = new URL(page ?? window.location.href);
|
|
134
|
-
url.hash = '#' + el.getAttribute('id');
|
|
135
|
-
const href = url.toString();
|
|
136
|
-
|
|
137
|
-
return {
|
|
138
|
-
// ID is the id attribute
|
|
139
|
-
// (ID may not be unique in a multi-page spec)
|
|
140
|
-
id: el.getAttribute('id'),
|
|
141
|
-
|
|
142
|
-
// Absolute URL with fragment
|
|
143
|
-
href,
|
|
144
|
-
|
|
145
|
-
// Linking text is given by the data-lt attribute if present, or it is the
|
|
146
|
-
// textual content
|
|
147
|
-
linkingText: el.hasAttribute('data-lt') ?
|
|
148
|
-
el.getAttribute('data-lt').split('|').map(normalize) :
|
|
149
|
-
[normalize(el.textContent)],
|
|
150
|
-
|
|
151
|
-
// Additional linking text can be defined for local references
|
|
152
|
-
localLinkingText: el.getAttribute('data-local-lt') ?
|
|
153
|
-
el.getAttribute('data-local-lt').split('|').map(normalize) :
|
|
154
|
-
[],
|
|
155
|
-
|
|
156
|
-
// Link type must be specified, or it is "dfn"
|
|
157
|
-
type: el.getAttribute('data-dfn-type') || 'dfn',
|
|
158
|
-
|
|
159
|
-
// Definition may be namespaced to other constructs. Note the list is not
|
|
160
|
-
// purely comma-separated due to function parameters. For instance,
|
|
161
|
-
// attribute value may be "method(foo,bar), method()"
|
|
162
|
-
for: el.getAttribute('data-dfn-for') ?
|
|
163
|
-
el.getAttribute('data-dfn-for').split(/,(?![^\(]*\))/).map(normalize) :
|
|
164
|
-
[],
|
|
165
|
-
|
|
166
|
-
// Definition is public if explicitly marked as exportable or if export has
|
|
167
|
-
// not been explicitly disallowed and its type is not "dfn"
|
|
168
|
-
access: (el.hasAttribute('data-export') ||
|
|
169
|
-
(!el.hasAttribute('data-noexport') &&
|
|
170
|
-
el.hasAttribute('data-dfn-type') &&
|
|
171
|
-
el.getAttribute('data-dfn-type') !== 'dfn')) ?
|
|
172
|
-
'public' : 'private',
|
|
173
|
-
|
|
174
|
-
// Whether the term is defined in a normative/informative section
|
|
175
|
-
informative: !!el.closest(informativeSelector),
|
|
176
|
-
|
|
177
|
-
// Heading under which the term is to be found
|
|
178
|
-
heading: idToHeading[href],
|
|
179
|
-
|
|
180
|
-
// Enclosing element under which the definition appears. Value can be one of
|
|
181
|
-
// "dt", "pre", "table", "heading", "note", "example", or "prose" (last one
|
|
182
|
-
// indicates that definition appears in the main body of the specification)
|
|
183
|
-
definedIn
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
export default function (spec, idToHeading = {}) {
|
|
188
|
-
const definitionsSelector = [
|
|
189
|
-
// re data-lt, see https://github.com/w3c/reffy/issues/336#issuecomment-650339747
|
|
190
|
-
'dfn[id]:not([data-lt=""])',
|
|
191
|
-
'h2[id][data-dfn-type]:not([data-lt=""])',
|
|
192
|
-
'h3[id][data-dfn-type]:not([data-lt=""])',
|
|
193
|
-
'h4[id][data-dfn-type]:not([data-lt=""])',
|
|
194
|
-
'h5[id][data-dfn-type]:not([data-lt=""])',
|
|
195
|
-
'h6[id][data-dfn-type]:not([data-lt=""])'
|
|
196
|
-
].join(',');
|
|
197
|
-
|
|
198
|
-
let extraDefinitions = [];
|
|
199
|
-
const shortname = (typeof spec === 'string') ? spec : spec.shortname;
|
|
200
|
-
switch (shortname) {
|
|
201
|
-
case "html":
|
|
202
|
-
preProcessHTML();
|
|
203
|
-
break;
|
|
204
|
-
case "ecmascript":
|
|
205
|
-
preProcessEcmascript();
|
|
206
|
-
break;
|
|
207
|
-
case "SVG2":
|
|
208
|
-
preProcessSVG2();
|
|
209
|
-
break;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
return [...document.querySelectorAll(definitionsSelector)]
|
|
213
|
-
.map(node => {
|
|
214
|
-
// 2021-06-21: Temporary preprocessing of invalid "idl" dfn type (used for
|
|
215
|
-
// internal slots) while fix for https://github.com/w3c/respec/issues/3644
|
|
216
|
-
// propagates to all EDs and /TR specs. To be dropped once crawls no
|
|
217
|
-
// longer produce warnings.
|
|
218
|
-
if (node.getAttribute('data-dfn-type') === 'idl') {
|
|
219
|
-
const linkingText = node.hasAttribute('data-lt') ?
|
|
220
|
-
node.getAttribute('data-lt').split('|').map(normalize) :
|
|
221
|
-
[normalize(node.textContent)];
|
|
222
|
-
node.setAttribute('data-dfn-type', linkingText[0].endsWith(')') ? 'method' : 'attribute');
|
|
223
|
-
console.warn('[reffy]', `Fixed invalid "idl" dfn type "${normalize(node.textContent)}"`);
|
|
224
|
-
}
|
|
225
|
-
return node;
|
|
226
|
-
})
|
|
227
|
-
.filter(hasValidType)
|
|
228
|
-
.map(node => definitionMapper(node, idToHeading));
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
function preProcessEcmascript() {
|
|
232
|
-
// Skip elements in sections marked as legacy
|
|
233
|
-
const legacySectionFilter = n => !n.closest("[legacy]");
|
|
234
|
-
|
|
235
|
-
const wrapWithDfn = (el) => {
|
|
236
|
-
// wrap with a dfn
|
|
237
|
-
const dfn = document.createElement("dfn");
|
|
238
|
-
for (let child of [...el.childNodes]) {
|
|
239
|
-
dfn.appendChild(child);
|
|
240
|
-
}
|
|
241
|
-
el.appendChild(dfn);
|
|
242
|
-
// set id
|
|
243
|
-
dfn.setAttribute("id", el.parentNode.getAttribute("id"));
|
|
244
|
-
dfn.dataset.ltNodefault = true;
|
|
245
|
-
return dfn;
|
|
246
|
-
};
|
|
247
|
-
|
|
248
|
-
const cleanMethodName = (name) => {
|
|
249
|
-
return name.replace(/\[/g, '')
|
|
250
|
-
.replace(/\]/g, '') // removing brackets used to mark optional args
|
|
251
|
-
.replace(/ \( */, '(')
|
|
252
|
-
.replace(/ *\)/, ')')
|
|
253
|
-
.replace(/ *,/g, ','); // trimming internal spaces
|
|
254
|
-
};
|
|
255
|
-
|
|
256
|
-
let definitionNames = new Set();
|
|
257
|
-
let idlTypes = {};
|
|
258
|
-
|
|
259
|
-
// We find the list of abstract methods
|
|
260
|
-
// to help with scoping abstract operations
|
|
261
|
-
let abstractMethods = {};
|
|
262
|
-
const abstractMethodCaptions = [...document.querySelectorAll("figcaption")]
|
|
263
|
-
.filter(el => el.textContent.match(/(abstract|additional) method/i) && el.parentNode.querySelector("emu-xref"));
|
|
264
|
-
for (const figcaption of abstractMethodCaptions) {
|
|
265
|
-
const scope = figcaption.querySelector("emu-xref").textContent;
|
|
266
|
-
const table = figcaption.parentNode.querySelector("tbody");
|
|
267
|
-
for (const td of table.querySelectorAll("tr td:first-child")) {
|
|
268
|
-
// We only consider the name of the method, not the potential parameters
|
|
269
|
-
// as they're not necessarily consistently named across
|
|
270
|
-
// the list and the definition
|
|
271
|
-
const methodName = td.textContent.trim().split('(')[0];
|
|
272
|
-
abstractMethods[methodName] = scope;
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
const sectionNumberRegExp = /^([A-Z]\.)?[0-9\.]+ /;
|
|
277
|
-
[...document.querySelectorAll("h1")]
|
|
278
|
-
.filter(legacySectionFilter)
|
|
279
|
-
.forEach(el => {
|
|
280
|
-
let dfnName = el.textContent.replace(sectionNumberRegExp, '').trim() ;// remove section number
|
|
281
|
-
const dfnId = el.parentNode.id;
|
|
282
|
-
if (dfnId.match(/-objects?$/) && dfnName.match(/ Objects?$/)) {
|
|
283
|
-
|
|
284
|
-
// Skip headings that look like object definitions, but aren't
|
|
285
|
-
const notObjectIds = ["sec-global-object", "sec-fundamental-objects", "sec-waiterlist-objects"];
|
|
286
|
-
if (notObjectIds.includes(dfnId)) return;
|
|
287
|
-
|
|
288
|
-
// only keep ids that match a credible pattern for object names
|
|
289
|
-
// i.e. a single word
|
|
290
|
-
// there are exceptions to that simple rule
|
|
291
|
-
// RegExp includes its expansion (regular expansion) in the id
|
|
292
|
-
// WeakRef is translated into weak-ref in the id
|
|
293
|
-
const objectsIdsExceptions = ["sec-regexp-regular-expression-objects", "sec-weak-ref-objects", "sec-aggregate-error-objects", "sec-finalization-registry-objects", "sec-async-function-objects"];
|
|
294
|
-
|
|
295
|
-
if (!dfnId.match(/sec-[a-z]+-objects?/)
|
|
296
|
-
&& !objectsIdsExceptions.includes(dfnId)
|
|
297
|
-
) return;
|
|
298
|
-
const dfn = wrapWithDfn(el);
|
|
299
|
-
// set data-lt
|
|
300
|
-
dfnName = dfnName
|
|
301
|
-
.replace(/^The /, '')
|
|
302
|
-
.replace(/ Objects?$/, '')
|
|
303
|
-
// regexp def includes "(Regular Expression)"
|
|
304
|
-
.replace(/ \([^\)]*\)/, '') ;
|
|
305
|
-
dfn.dataset.lt = dfnName;
|
|
306
|
-
|
|
307
|
-
// FIXME
|
|
308
|
-
// These interfaces are also defined in WebIDL, which in general is
|
|
309
|
-
// the prefered source for these terms
|
|
310
|
-
// Because bikeshed does not yet support spec-specific imports,
|
|
311
|
-
// we hide these terms as not exported
|
|
312
|
-
// cf https://github.com/w3c/reffy/pull/732#issuecomment-925950287
|
|
313
|
-
const exportExceptions = [ "Promise", "DataView", "ArrayBuffer" ];
|
|
314
|
-
if (exportExceptions.includes(dfnName)) {
|
|
315
|
-
dfn.dataset.noexport = "";
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
if (dfnName.match(/^[A-Z]/)) {
|
|
319
|
-
// set dfn-type
|
|
320
|
-
if (dfnName.match(/Error$/)) {
|
|
321
|
-
dfn.dataset.dfnType = "exception";
|
|
322
|
-
} else if (!el.parentNode.querySelector('[id$="constructor"]')) {
|
|
323
|
-
// Objects without constructors match to the namespace type
|
|
324
|
-
dfn.dataset.dfnType = "namespace";
|
|
325
|
-
} else {
|
|
326
|
-
dfn.dataset.dfnType = "interface";
|
|
327
|
-
}
|
|
328
|
-
// We keep track of types associated with a name
|
|
329
|
-
// to associate the same type to the relevant intrinsic object
|
|
330
|
-
// à la %Math%
|
|
331
|
-
idlTypes[dfnName] = dfn.dataset.dfnType;
|
|
332
|
-
}
|
|
333
|
-
definitionNames.add(dfnName);
|
|
334
|
-
} else if (dfnId.match(/-[a-z]+error$/) && !dfnName.match(/\(/)) {
|
|
335
|
-
const dfn = wrapWithDfn(el);
|
|
336
|
-
dfn.dataset.lt = dfnName;
|
|
337
|
-
dfn.dataset.dfnType = "exception";
|
|
338
|
-
definitionNames.add(dfnName);
|
|
339
|
-
idlTypes[dfnName] = dfn.dataset.dfnType;
|
|
340
|
-
} else if (dfnId.match(/[-\.]prototype[-\.]/)) {
|
|
341
|
-
// methods and attributes on objects
|
|
342
|
-
|
|
343
|
-
// Skip headings with a space and no parenthesis
|
|
344
|
-
// (they mention prototype but aren't a prototype property def)
|
|
345
|
-
// with the exception of "set " and "get " headings
|
|
346
|
-
// (which describe setters and getters)
|
|
347
|
-
if (!dfnName.match(/\(/) && (dfnName.match(/ /) && !dfnName.match(/^[gs]et /))) return;
|
|
348
|
-
|
|
349
|
-
// Skip unscoped internal methods à la [[SetPrototypeOf]](V)
|
|
350
|
-
if (dfnName.match(/\[\[/)) return;
|
|
351
|
-
|
|
352
|
-
// Skip symbol-based property definitions;
|
|
353
|
-
// not clear they're useful as externally referenceable names
|
|
354
|
-
if (dfnName.match(/@@/)) return;
|
|
355
|
-
|
|
356
|
-
// Skip .constructor as that cannot be considered as an attribute
|
|
357
|
-
if (dfnName.match(/\.constructor$/)) return;
|
|
358
|
-
|
|
359
|
-
const dfn = wrapWithDfn(el);
|
|
360
|
-
// set definition scope
|
|
361
|
-
dfn.dataset.dfnFor = dfnName.replace(/\.prototype\..*/, '')
|
|
362
|
-
.replace(/^[gs]et /, ''); // remove "get"/"set" markers
|
|
363
|
-
|
|
364
|
-
// Remove parent object prototype (set as scope)
|
|
365
|
-
dfnName = dfnName.replace(/.*\.prototype\./, '');
|
|
366
|
-
|
|
367
|
-
dfn.dataset.lt = dfnName;
|
|
368
|
-
// set dfn-type
|
|
369
|
-
if (dfn.dataset.lt.match(/\(/)) {
|
|
370
|
-
dfnName = cleanMethodName(dfnName);
|
|
371
|
-
dfn.dataset.lt = dfnName;
|
|
372
|
-
dfn.dataset.dfnType = "method";
|
|
373
|
-
} else {
|
|
374
|
-
dfn.dataset.dfnType = "attribute";
|
|
375
|
-
}
|
|
376
|
-
} else if (el.closest("#sec-value-properties-of-the-global-object")) {
|
|
377
|
-
// properties of the global object
|
|
378
|
-
if (el.id !== "#sec-value-properties-of-the-global-object"){
|
|
379
|
-
const dfn = wrapWithDfn(el);
|
|
380
|
-
dfn.dataset.lt = dfnName;
|
|
381
|
-
dfn.dataset.dfnType = "attribute";
|
|
382
|
-
dfn.dataset.dfnFor = "globalThis";
|
|
383
|
-
}
|
|
384
|
-
} else {
|
|
385
|
-
// We handle other headings that look like a method / property
|
|
386
|
-
// on an object instance (rather than its prototype)
|
|
387
|
-
// or an abstract op
|
|
388
|
-
|
|
389
|
-
// if there is already a dfn element, we move on
|
|
390
|
-
if (el.querySelector("dfn")) return;
|
|
391
|
-
|
|
392
|
-
// only dealing with well-known patterns
|
|
393
|
-
if (!dfnName.match(/^[a-z]+\.[a-z]+/i) // à la JSON.parse
|
|
394
|
-
&& !dfnName.match(/^([a-z]+)+ *\(/i) // à la ArrayCreate ( or decodeURI (
|
|
395
|
-
) return;
|
|
396
|
-
// Skip symbol-based property definitions
|
|
397
|
-
if (dfnName.match(/@@/)) return;
|
|
398
|
-
|
|
399
|
-
// Skip .prototype as that cannot be considered
|
|
400
|
-
// as an attribute
|
|
401
|
-
if (dfnName.match(/\.prototype$/)) return;
|
|
402
|
-
|
|
403
|
-
// Skip headings where foo.bar appears as part of a longer phrase
|
|
404
|
-
if (!dfnName.match(/\(/) && dfnName.match(/ /)) return;
|
|
405
|
-
|
|
406
|
-
// redundant definitions of constructors on the global object
|
|
407
|
-
// e.g. "Array ( . . . )"
|
|
408
|
-
if (dfnName.match(/\. \. \./)) return;
|
|
409
|
-
|
|
410
|
-
const dfn = wrapWithDfn(el);
|
|
411
|
-
|
|
412
|
-
if (dfnName.match(/^[a-z]+\.[a-z]+/i)) {
|
|
413
|
-
// set definition scope
|
|
414
|
-
// This assumes that such methods and attributes are only defined
|
|
415
|
-
// one-level deep from the global scope
|
|
416
|
-
dfn.dataset.dfnFor = dfnName.replace(/\..*$/, '');
|
|
417
|
-
dfnName = dfnName.replace(dfn.dataset.dfnFor + ".", '');
|
|
418
|
-
if (dfnName.match(/\(/)) {
|
|
419
|
-
dfnName = cleanMethodName(dfnName);
|
|
420
|
-
dfn.dataset.lt = dfnName;
|
|
421
|
-
dfn.dataset.dfnType = "method";
|
|
422
|
-
} else {
|
|
423
|
-
dfn.dataset.lt = dfnName;
|
|
424
|
-
if (dfnName.match(/^[A-Z]+$/)) {
|
|
425
|
-
dfn.dataset.dfnType = "const";
|
|
426
|
-
} else {
|
|
427
|
-
dfn.dataset.dfnType = "attribute";
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
} else if (dfnName.match(/^([A-Z]+[a-z]*)+ *\(/)) { // Abstract ops à la ArrayCreate or global constructor
|
|
431
|
-
dfnName = cleanMethodName(dfnName);
|
|
432
|
-
dfn.dataset.lt = dfnName;
|
|
433
|
-
const opName = dfnName.split('(')[0];
|
|
434
|
-
|
|
435
|
-
// distinguish global constructors from abstract operations
|
|
436
|
-
if (idlTypes[opName]) {
|
|
437
|
-
dfn.dataset.dfnType = "constructor";
|
|
438
|
-
dfn.dataset.dfnFor = opName;
|
|
439
|
-
} else {
|
|
440
|
-
// If the name is listed as an Abstract Method
|
|
441
|
-
// we set the dfn-for accordingly
|
|
442
|
-
if (abstractMethods[opName]) {
|
|
443
|
-
dfn.dataset.dfnFor = abstractMethods[opName];
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
dfn.dataset.dfnType = "abstract-op";
|
|
447
|
-
}
|
|
448
|
-
} else { // methods of the global object
|
|
449
|
-
dfnName = cleanMethodName(dfnName);
|
|
450
|
-
dfn.dataset.lt = dfnName;
|
|
451
|
-
dfn.dataset.dfnType = "method";
|
|
452
|
-
dfn.dataset.dfnFor = "globalThis";
|
|
453
|
-
}
|
|
454
|
-
definitionNames.add(dfnName);
|
|
455
|
-
}
|
|
456
|
-
});
|
|
457
|
-
// Extract abstract operations from <emu-eqn> with aoid attribute
|
|
458
|
-
[...document.querySelectorAll("emu-eqn[aoid]")]
|
|
459
|
-
.filter(legacySectionFilter)
|
|
460
|
-
.forEach(el => {
|
|
461
|
-
// Skip definitions of constant values (e.g. msPerDay)
|
|
462
|
-
if (el.textContent.match(/=/)) return;
|
|
463
|
-
const dfn = wrapWithDfn(el);
|
|
464
|
-
dfn.dataset.lt = el.getAttribute("aoid");
|
|
465
|
-
dfn.dataset.dfnType = "abstract-op";
|
|
466
|
-
dfn.id = el.id;
|
|
467
|
-
});
|
|
468
|
-
|
|
469
|
-
// Extract State Components from tables
|
|
470
|
-
[...document.querySelectorAll("figure > table")]
|
|
471
|
-
.filter(legacySectionFilter)
|
|
472
|
-
.forEach(el => {
|
|
473
|
-
const title = el.parentNode.querySelector("figcaption")?.textContent || "";
|
|
474
|
-
if (!title.match(/state components for/i)) return;
|
|
475
|
-
const scope = title.replace(/^.*state components for/i, '').trim();
|
|
476
|
-
for (const td of el.querySelectorAll("tr td:first-child")) {
|
|
477
|
-
const dfn = wrapWithDfn(td);
|
|
478
|
-
dfn.dataset.dfnFor = scope;
|
|
479
|
-
dfn.id = el.closest("emu-table[id],emu-clause[id]").id;
|
|
480
|
-
}
|
|
481
|
-
});
|
|
482
|
-
|
|
483
|
-
[...document.querySelectorAll("dfn")]
|
|
484
|
-
.filter(legacySectionFilter)
|
|
485
|
-
.forEach(el => {
|
|
486
|
-
// Skip definitions in conformance page and conventions page
|
|
487
|
-
if (el.closest('section[data-reffy-page$="conformance.html"]') ||
|
|
488
|
-
el.closest('section[data-reffy-page$="notational-conventions.html"]')) {
|
|
489
|
-
el.removeAttribute("id");
|
|
490
|
-
return;
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
// rely on the aoid attribute as a hint we're dealing
|
|
494
|
-
// with an abstract-op
|
|
495
|
-
if (el.getAttribute("aoid")) {
|
|
496
|
-
el.dataset.dfnType = "abstract-op";
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
// Mark well-known intrinsic objects as the same type as their visible object (if set), defaulting to "interface"
|
|
500
|
-
if (el.textContent.match(/^%[A-Z].*%$/)) {
|
|
501
|
-
el.dataset.dfnType = idlTypes[el.textContent.replace(/%/g, '')] || "interface";
|
|
502
|
-
definitionNames.add(el.textContent.trim());
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
// %names% in the global object section are operations of the globalThis object
|
|
506
|
-
if (el.closest('[data-reffy-page$="global-object.html"]') && el.textContent.match(/^%[a-z]+%/i)) {
|
|
507
|
-
el.dataset.dfnFor = "globalThis";
|
|
508
|
-
// TODO: this doesn't capture the arguments
|
|
509
|
-
el.dataset.dfnType = "method";
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
// Mark well-known symbols as "const"
|
|
513
|
-
// for lack of a better type, and as the WebIDL spec has been doing
|
|
514
|
-
if (el.textContent.match(/^@@[a-z]*$/i)) {
|
|
515
|
-
el.dataset.dfnType = "const";
|
|
516
|
-
}
|
|
517
|
-
if (el.getAttribute("variants")) {
|
|
518
|
-
el.dataset.lt = (el.dataset.lt ?? el.textContent.trim()) + "|" + el.getAttribute("variants");
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
// Skip definitions that have already been identified
|
|
522
|
-
// with a more specific typing
|
|
523
|
-
if (!el.dataset.dfnType) {
|
|
524
|
-
// we already have a matching typed definition
|
|
525
|
-
if (definitionNames.has(el.textContent.trim())) return;
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
// If the <dfn> has no id, we attach it the one from the closest
|
|
529
|
-
// <emu-clause> with an id
|
|
530
|
-
// Note that this means several definitions can share the same id
|
|
531
|
-
if (!el.getAttribute("id")) {
|
|
532
|
-
if (el.closest("emu-clause[id]")) {
|
|
533
|
-
el.setAttribute("id", el.closest("emu-clause").getAttribute("id"));
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
// Any generic <dfn> not previously filtered out
|
|
538
|
-
// is deemed to be exported, scoped to ECMAScript
|
|
539
|
-
if (!el.dataset.dfnType) {
|
|
540
|
-
if (!el.dataset.dfnFor) {
|
|
541
|
-
el.dataset.dfnFor = "ECMAScript";
|
|
542
|
-
}
|
|
543
|
-
el.dataset.export = "";
|
|
544
|
-
}
|
|
545
|
-
});
|
|
546
|
-
// Another pass of clean up for duplicates
|
|
547
|
-
// This cannot be done in the first pass
|
|
548
|
-
// because %Foo.prototype% does not necessarily get identified before
|
|
549
|
-
// the equivalent " prototype object" dfn
|
|
550
|
-
|
|
551
|
-
[...document.querySelectorAll("dfn[id][data-export]")]
|
|
552
|
-
.filter(legacySectionFilter)
|
|
553
|
-
.forEach(dfn => {
|
|
554
|
-
// we have the syntactic equivalent %x.prototype%
|
|
555
|
-
let m = dfn.textContent.trim().match(/^(.*) prototype( object)?$/);
|
|
556
|
-
if (m && definitionNames.has(`%${m[1].trim()}.prototype%`)) {
|
|
557
|
-
dfn.removeAttribute("id");
|
|
558
|
-
delete dfn.dataset.export;
|
|
559
|
-
return;
|
|
560
|
-
}
|
|
561
|
-
});
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
function preProcessHTML() {
|
|
565
|
-
const headingSelector = [
|
|
566
|
-
'h2[id]:not([data-dfn-type]) dfn',
|
|
567
|
-
'h3[id]:not([data-dfn-type]) dfn',
|
|
568
|
-
'h4[id]:not([data-dfn-type]) dfn',
|
|
569
|
-
'h5[id]:not([data-dfn-type]) dfn',
|
|
570
|
-
'h6[id]:not([data-dfn-type]) dfn'
|
|
571
|
-
].join(',');
|
|
572
|
-
|
|
573
|
-
// we copy the id on the dfn when it is set on the surrounding heading
|
|
574
|
-
document.querySelectorAll(headingSelector)
|
|
575
|
-
.forEach(el => {
|
|
576
|
-
const headingId = el.closest("h2, h3, h4, h5, h6").id;
|
|
577
|
-
if (!el.id) {
|
|
578
|
-
el.id = headingId;
|
|
579
|
-
}
|
|
580
|
-
});
|
|
581
|
-
|
|
582
|
-
// all the definitions in indices.html are non-normative, so we skip them
|
|
583
|
-
// to avoid having to properly type them
|
|
584
|
-
// they're not all that interesting
|
|
585
|
-
document.querySelectorAll('section[data-reffy-page$="indices.html"] dfn[id]')
|
|
586
|
-
.forEach(el => {
|
|
587
|
-
el.dataset.dfnSkip = true;
|
|
588
|
-
});
|
|
589
|
-
|
|
590
|
-
document.querySelectorAll("dfn[id]:not([data-dfn-type]):not([data-skip])")
|
|
591
|
-
.forEach(el => {
|
|
592
|
-
// Hard coded rules for special ids
|
|
593
|
-
// dom-style is defined elsewhere
|
|
594
|
-
if (el.id === "dom-style") {
|
|
595
|
-
el.dataset.dfnType = 'attribute';
|
|
596
|
-
el.dataset.dfnFor = 'HTMLElement';
|
|
597
|
-
el.dataset.noexport = "";
|
|
598
|
-
return;
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
// If there is a link, we assume this documents an imported definition
|
|
602
|
-
// so we make it ignored by removing the id
|
|
603
|
-
if (el.querySelector('a[href^="http"]')) {
|
|
604
|
-
return;
|
|
605
|
-
}
|
|
606
|
-
});
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
function preProcessSVG2() {
|
|
610
|
-
const idl = extractWebIdl();
|
|
611
|
-
const idlTree = parse(idl);
|
|
612
|
-
const idlInterfaces = idlTree.filter(item => item.type === "interface" || item.type === "interface mixin");
|
|
613
|
-
|
|
614
|
-
// the only element definition not properly marked up in the SVG spec
|
|
615
|
-
const linkHeading = document.getElementById("LinkElement");
|
|
616
|
-
if (linkHeading && !linkHeading.dataset.dfnType) {
|
|
617
|
-
linkHeading.dataset.dfnType = "element";
|
|
618
|
-
linkHeading.dataset.lt = "link";
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
document.querySelectorAll(".attrdef dfn[id]:not([data-dfn-type]):not([data-skip])")
|
|
622
|
-
.forEach(el => {
|
|
623
|
-
el.dataset.dfnType = "element-attr";
|
|
624
|
-
const attrDesc = document.querySelector('[data-reffy-page$="attindex.html"] th span.attr-name a[href$="#' + el.id + '"]');
|
|
625
|
-
if (attrDesc) {
|
|
626
|
-
el.dataset.dfnFor = attrDesc.closest('tr').querySelector('td').textContent;
|
|
627
|
-
} else {
|
|
628
|
-
console.error("Could not find description for " + el.textContent);
|
|
629
|
-
}
|
|
630
|
-
});
|
|
631
|
-
document.querySelectorAll("dt[id] > .adef, dt[id] > .property")
|
|
632
|
-
.forEach(el => {
|
|
633
|
-
const dt = el.parentNode;
|
|
634
|
-
const newDt = document.createElement("dt");
|
|
635
|
-
const dfn = document.createElement("dfn");
|
|
636
|
-
dfn.id = dt.id;
|
|
637
|
-
dfn.dataset.dfnType = el.classList.contains("adef") ? "element-attr" : "property";
|
|
638
|
-
const indexPage = el.classList.contains("adef") ? "attindex.html" : "propidx.html";
|
|
639
|
-
const attrDesc = document.querySelector('[data-reffy-page$="' + indexPage + '"] th a[href$="#' + dfn.id + '"]');
|
|
640
|
-
if (attrDesc) {
|
|
641
|
-
// TODO: this doesn't deal with grouping of elements, e.g. "text content elements"
|
|
642
|
-
dfn.dataset.dfnFor = [...attrDesc.closest('tr').querySelectorAll('span.element-name a')].map (n => n.textContent).join(',');
|
|
643
|
-
} else {
|
|
644
|
-
console.error("Could not find description for " + el.textContent + "/" + dfn.id);
|
|
645
|
-
}
|
|
646
|
-
dfn.textContent = el.textContent;
|
|
647
|
-
newDt.appendChild(dfn);
|
|
648
|
-
dt.replaceWith(newDt);
|
|
649
|
-
});
|
|
650
|
-
document.querySelectorAll('b[id^="__svg__"]').forEach(el => {
|
|
651
|
-
const [,, containername, membername] = el.id.split('__');
|
|
652
|
-
if (containername && membername) {
|
|
653
|
-
let container = idlTree.find(i => i.name === containername);
|
|
654
|
-
if (container) {
|
|
655
|
-
let member = container.members.find(m => m.name === membername);
|
|
656
|
-
if (member) {
|
|
657
|
-
const dfn = document.createElement("dfn");
|
|
658
|
-
dfn.id = el.id;
|
|
659
|
-
dfn.textContent = el.textContent;
|
|
660
|
-
dfn.dataset.dfnFor = containername;
|
|
661
|
-
dfn.dataset.dfnType = member.type === "operation" ? "method" : member.type;
|
|
662
|
-
el.replaceWith(dfn);
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
});
|
|
667
|
-
document.querySelectorAll('h3[id^="Interface"]:not([data-dfn-type])').forEach(el => {
|
|
668
|
-
const name = el.id.slice("Interface".length);
|
|
669
|
-
if (idlTree.find(i => i.name === name && i.type === "interface")) {
|
|
670
|
-
el.dataset.dfnType = "interface";
|
|
671
|
-
el.dataset.lt = name;
|
|
672
|
-
}
|
|
673
|
-
});
|
|
674
|
-
document.querySelectorAll('b[id]:not([data-dfn-type])').forEach(el => {
|
|
675
|
-
const name = el.textContent;
|
|
676
|
-
const idlItem = idlTree.find(i => i.name === name) ;
|
|
677
|
-
if (idlItem) {
|
|
678
|
-
const dfn = document.createElement("dfn");
|
|
679
|
-
dfn.id = el.id;
|
|
680
|
-
dfn.dataset.dfnType = idlItem.type;
|
|
681
|
-
dfn.textContent = el.textContent;
|
|
682
|
-
el.replaceWith(dfn);
|
|
683
|
-
}
|
|
684
|
-
});
|
|
685
|
-
|
|
686
|
-
}
|
|
1
|
+
import extractWebIdl from './extract-webidl.mjs';
|
|
2
|
+
import informativeSelector from './informative-selector.mjs';
|
|
3
|
+
import {parse} from "../../node_modules/webidl2/index.js";
|
|
4
|
+
/**
|
|
5
|
+
* Extract definitions in the spec that follow the "Definitions data model":
|
|
6
|
+
* https://tabatkins.github.io/bikeshed/#dfn-contract
|
|
7
|
+
*
|
|
8
|
+
* Each definition returned by the function will have the following properties:
|
|
9
|
+
* - id: The local ID in the DOM. Should be unique within a spec page.
|
|
10
|
+
* - href: The absolute URL to the definition.
|
|
11
|
+
* - linkingText: List of linking phrases for references.
|
|
12
|
+
* - localLinkingText: List of linking phrases for local references only.
|
|
13
|
+
* - type: The definition type. One of the values in
|
|
14
|
+
* https://tabatkins.github.io/bikeshed/#dfn-types
|
|
15
|
+
* - for: The list of namespaces for the definition
|
|
16
|
+
* - access: "public" when definition can be referenced by other specifications,
|
|
17
|
+
* "private" when it should be viewed as a local definition.
|
|
18
|
+
* - informative: true when definition appears in an informative section,
|
|
19
|
+
* false if it is normative
|
|
20
|
+
* - heading: Heading under which the term is to be found. An object with "id",
|
|
21
|
+
* "title", and "number" properties
|
|
22
|
+
* - definedIn: An indication of where the definition appears in the spec. Value
|
|
23
|
+
* can be one of "dt", "pre", "table", "heading", "note", "example", or
|
|
24
|
+
* "prose" (last one indicates that definition appears in the main body of
|
|
25
|
+
* the spec)
|
|
26
|
+
*
|
|
27
|
+
* @function
|
|
28
|
+
* @public
|
|
29
|
+
* @return {Array(Object)} An Array of definitions
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
function normalize(str) {
|
|
33
|
+
return str.trim().replace(/\s+/g, ' ');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Valid types defined in https://tabatkins.github.io/bikeshed/#dfn-types
|
|
37
|
+
// (+ "namespace" and "event" which are not yet in the doc)
|
|
38
|
+
function hasValidType(el) {
|
|
39
|
+
const validDfnTypes = [
|
|
40
|
+
// CSS types
|
|
41
|
+
'property',
|
|
42
|
+
'descriptor',
|
|
43
|
+
'value',
|
|
44
|
+
'type',
|
|
45
|
+
'at-rule',
|
|
46
|
+
'function',
|
|
47
|
+
'selector',
|
|
48
|
+
|
|
49
|
+
// Web IDL types
|
|
50
|
+
'namespace',
|
|
51
|
+
'interface',
|
|
52
|
+
'constructor',
|
|
53
|
+
'method',
|
|
54
|
+
'argument',
|
|
55
|
+
'attribute',
|
|
56
|
+
'callback',
|
|
57
|
+
'dictionary',
|
|
58
|
+
'dict-member',
|
|
59
|
+
'enum',
|
|
60
|
+
'enum-value',
|
|
61
|
+
'exception',
|
|
62
|
+
'const',
|
|
63
|
+
'typedef',
|
|
64
|
+
'stringifier',
|
|
65
|
+
'serializer',
|
|
66
|
+
'iterator',
|
|
67
|
+
'maplike',
|
|
68
|
+
'setlike',
|
|
69
|
+
'extended-attribute',
|
|
70
|
+
'event',
|
|
71
|
+
|
|
72
|
+
// Element types
|
|
73
|
+
'element',
|
|
74
|
+
'element-state',
|
|
75
|
+
'element-attr',
|
|
76
|
+
'attr-value',
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
// URL scheme
|
|
80
|
+
'scheme',
|
|
81
|
+
|
|
82
|
+
// HTTP header
|
|
83
|
+
'http-header',
|
|
84
|
+
|
|
85
|
+
// Grammar type
|
|
86
|
+
'grammar',
|
|
87
|
+
|
|
88
|
+
// "English" terms
|
|
89
|
+
'abstract-op',
|
|
90
|
+
'dfn'
|
|
91
|
+
];
|
|
92
|
+
|
|
93
|
+
const type = el.getAttribute('data-dfn-type') ?? 'dfn';
|
|
94
|
+
const isValid = validDfnTypes.includes(type);
|
|
95
|
+
if (!isValid) {
|
|
96
|
+
console.warn('[reffy]', `"${type}" is an invalid dfn type for "${normalize(el.textContent)}"`);
|
|
97
|
+
}
|
|
98
|
+
return isValid;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
function definitionMapper(el, idToHeading) {
|
|
103
|
+
let definedIn = 'prose';
|
|
104
|
+
const enclosingEl = el.closest('dt,pre,table,h1,h2,h3,h4,h5,h6,.note,.example') || el;
|
|
105
|
+
switch (enclosingEl.nodeName) {
|
|
106
|
+
case 'DT':
|
|
107
|
+
case 'PRE':
|
|
108
|
+
case 'TABLE':
|
|
109
|
+
definedIn = enclosingEl.nodeName.toLowerCase();
|
|
110
|
+
break;
|
|
111
|
+
case 'H1':
|
|
112
|
+
case 'H2':
|
|
113
|
+
case 'H3':
|
|
114
|
+
case 'H4':
|
|
115
|
+
case 'H5':
|
|
116
|
+
case 'H6':
|
|
117
|
+
definedIn = 'heading';
|
|
118
|
+
break;
|
|
119
|
+
default:
|
|
120
|
+
if (enclosingEl.classList.contains('note')) {
|
|
121
|
+
definedIn = 'note';
|
|
122
|
+
}
|
|
123
|
+
else if (enclosingEl.classList.contains('example')) {
|
|
124
|
+
definedIn = 'example';
|
|
125
|
+
}
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Compute the absolute URL with fragment
|
|
130
|
+
// (Note the crawler merges pages of a multi-page spec in the first page
|
|
131
|
+
// to ease parsing logic, and we want to get back to the URL of the page)
|
|
132
|
+
const page = el.closest('[data-reffy-page]')?.getAttribute('data-reffy-page');
|
|
133
|
+
const url = new URL(page ?? window.location.href);
|
|
134
|
+
url.hash = '#' + el.getAttribute('id');
|
|
135
|
+
const href = url.toString();
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
// ID is the id attribute
|
|
139
|
+
// (ID may not be unique in a multi-page spec)
|
|
140
|
+
id: el.getAttribute('id'),
|
|
141
|
+
|
|
142
|
+
// Absolute URL with fragment
|
|
143
|
+
href,
|
|
144
|
+
|
|
145
|
+
// Linking text is given by the data-lt attribute if present, or it is the
|
|
146
|
+
// textual content
|
|
147
|
+
linkingText: el.hasAttribute('data-lt') ?
|
|
148
|
+
el.getAttribute('data-lt').split('|').map(normalize) :
|
|
149
|
+
[normalize(el.textContent)],
|
|
150
|
+
|
|
151
|
+
// Additional linking text can be defined for local references
|
|
152
|
+
localLinkingText: el.getAttribute('data-local-lt') ?
|
|
153
|
+
el.getAttribute('data-local-lt').split('|').map(normalize) :
|
|
154
|
+
[],
|
|
155
|
+
|
|
156
|
+
// Link type must be specified, or it is "dfn"
|
|
157
|
+
type: el.getAttribute('data-dfn-type') || 'dfn',
|
|
158
|
+
|
|
159
|
+
// Definition may be namespaced to other constructs. Note the list is not
|
|
160
|
+
// purely comma-separated due to function parameters. For instance,
|
|
161
|
+
// attribute value may be "method(foo,bar), method()"
|
|
162
|
+
for: el.getAttribute('data-dfn-for') ?
|
|
163
|
+
el.getAttribute('data-dfn-for').split(/,(?![^\(]*\))/).map(normalize) :
|
|
164
|
+
[],
|
|
165
|
+
|
|
166
|
+
// Definition is public if explicitly marked as exportable or if export has
|
|
167
|
+
// not been explicitly disallowed and its type is not "dfn"
|
|
168
|
+
access: (el.hasAttribute('data-export') ||
|
|
169
|
+
(!el.hasAttribute('data-noexport') &&
|
|
170
|
+
el.hasAttribute('data-dfn-type') &&
|
|
171
|
+
el.getAttribute('data-dfn-type') !== 'dfn')) ?
|
|
172
|
+
'public' : 'private',
|
|
173
|
+
|
|
174
|
+
// Whether the term is defined in a normative/informative section
|
|
175
|
+
informative: !!el.closest(informativeSelector),
|
|
176
|
+
|
|
177
|
+
// Heading under which the term is to be found
|
|
178
|
+
heading: idToHeading[href],
|
|
179
|
+
|
|
180
|
+
// Enclosing element under which the definition appears. Value can be one of
|
|
181
|
+
// "dt", "pre", "table", "heading", "note", "example", or "prose" (last one
|
|
182
|
+
// indicates that definition appears in the main body of the specification)
|
|
183
|
+
definedIn
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export default function (spec, idToHeading = {}) {
|
|
188
|
+
const definitionsSelector = [
|
|
189
|
+
// re data-lt, see https://github.com/w3c/reffy/issues/336#issuecomment-650339747
|
|
190
|
+
'dfn[id]:not([data-lt=""])',
|
|
191
|
+
'h2[id][data-dfn-type]:not([data-lt=""])',
|
|
192
|
+
'h3[id][data-dfn-type]:not([data-lt=""])',
|
|
193
|
+
'h4[id][data-dfn-type]:not([data-lt=""])',
|
|
194
|
+
'h5[id][data-dfn-type]:not([data-lt=""])',
|
|
195
|
+
'h6[id][data-dfn-type]:not([data-lt=""])'
|
|
196
|
+
].join(',');
|
|
197
|
+
|
|
198
|
+
let extraDefinitions = [];
|
|
199
|
+
const shortname = (typeof spec === 'string') ? spec : spec.shortname;
|
|
200
|
+
switch (shortname) {
|
|
201
|
+
case "html":
|
|
202
|
+
preProcessHTML();
|
|
203
|
+
break;
|
|
204
|
+
case "ecmascript":
|
|
205
|
+
preProcessEcmascript();
|
|
206
|
+
break;
|
|
207
|
+
case "SVG2":
|
|
208
|
+
preProcessSVG2();
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return [...document.querySelectorAll(definitionsSelector)]
|
|
213
|
+
.map(node => {
|
|
214
|
+
// 2021-06-21: Temporary preprocessing of invalid "idl" dfn type (used for
|
|
215
|
+
// internal slots) while fix for https://github.com/w3c/respec/issues/3644
|
|
216
|
+
// propagates to all EDs and /TR specs. To be dropped once crawls no
|
|
217
|
+
// longer produce warnings.
|
|
218
|
+
if (node.getAttribute('data-dfn-type') === 'idl') {
|
|
219
|
+
const linkingText = node.hasAttribute('data-lt') ?
|
|
220
|
+
node.getAttribute('data-lt').split('|').map(normalize) :
|
|
221
|
+
[normalize(node.textContent)];
|
|
222
|
+
node.setAttribute('data-dfn-type', linkingText[0].endsWith(')') ? 'method' : 'attribute');
|
|
223
|
+
console.warn('[reffy]', `Fixed invalid "idl" dfn type "${normalize(node.textContent)}"`);
|
|
224
|
+
}
|
|
225
|
+
return node;
|
|
226
|
+
})
|
|
227
|
+
.filter(hasValidType)
|
|
228
|
+
.map(node => definitionMapper(node, idToHeading));
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function preProcessEcmascript() {
|
|
232
|
+
// Skip elements in sections marked as legacy
|
|
233
|
+
const legacySectionFilter = n => !n.closest("[legacy]");
|
|
234
|
+
|
|
235
|
+
const wrapWithDfn = (el) => {
|
|
236
|
+
// wrap with a dfn
|
|
237
|
+
const dfn = document.createElement("dfn");
|
|
238
|
+
for (let child of [...el.childNodes]) {
|
|
239
|
+
dfn.appendChild(child);
|
|
240
|
+
}
|
|
241
|
+
el.appendChild(dfn);
|
|
242
|
+
// set id
|
|
243
|
+
dfn.setAttribute("id", el.parentNode.getAttribute("id"));
|
|
244
|
+
dfn.dataset.ltNodefault = true;
|
|
245
|
+
return dfn;
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
const cleanMethodName = (name) => {
|
|
249
|
+
return name.replace(/\[/g, '')
|
|
250
|
+
.replace(/\]/g, '') // removing brackets used to mark optional args
|
|
251
|
+
.replace(/ \( */, '(')
|
|
252
|
+
.replace(/ *\)/, ')')
|
|
253
|
+
.replace(/ *,/g, ','); // trimming internal spaces
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
let definitionNames = new Set();
|
|
257
|
+
let idlTypes = {};
|
|
258
|
+
|
|
259
|
+
// We find the list of abstract methods
|
|
260
|
+
// to help with scoping abstract operations
|
|
261
|
+
let abstractMethods = {};
|
|
262
|
+
const abstractMethodCaptions = [...document.querySelectorAll("figcaption")]
|
|
263
|
+
.filter(el => el.textContent.match(/(abstract|additional) method/i) && el.parentNode.querySelector("emu-xref"));
|
|
264
|
+
for (const figcaption of abstractMethodCaptions) {
|
|
265
|
+
const scope = figcaption.querySelector("emu-xref").textContent;
|
|
266
|
+
const table = figcaption.parentNode.querySelector("tbody");
|
|
267
|
+
for (const td of table.querySelectorAll("tr td:first-child")) {
|
|
268
|
+
// We only consider the name of the method, not the potential parameters
|
|
269
|
+
// as they're not necessarily consistently named across
|
|
270
|
+
// the list and the definition
|
|
271
|
+
const methodName = td.textContent.trim().split('(')[0];
|
|
272
|
+
abstractMethods[methodName] = scope;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const sectionNumberRegExp = /^([A-Z]\.)?[0-9\.]+ /;
|
|
277
|
+
[...document.querySelectorAll("h1")]
|
|
278
|
+
.filter(legacySectionFilter)
|
|
279
|
+
.forEach(el => {
|
|
280
|
+
let dfnName = el.textContent.replace(sectionNumberRegExp, '').trim() ;// remove section number
|
|
281
|
+
const dfnId = el.parentNode.id;
|
|
282
|
+
if (dfnId.match(/-objects?$/) && dfnName.match(/ Objects?$/)) {
|
|
283
|
+
|
|
284
|
+
// Skip headings that look like object definitions, but aren't
|
|
285
|
+
const notObjectIds = ["sec-global-object", "sec-fundamental-objects", "sec-waiterlist-objects"];
|
|
286
|
+
if (notObjectIds.includes(dfnId)) return;
|
|
287
|
+
|
|
288
|
+
// only keep ids that match a credible pattern for object names
|
|
289
|
+
// i.e. a single word
|
|
290
|
+
// there are exceptions to that simple rule
|
|
291
|
+
// RegExp includes its expansion (regular expansion) in the id
|
|
292
|
+
// WeakRef is translated into weak-ref in the id
|
|
293
|
+
const objectsIdsExceptions = ["sec-regexp-regular-expression-objects", "sec-weak-ref-objects", "sec-aggregate-error-objects", "sec-finalization-registry-objects", "sec-async-function-objects"];
|
|
294
|
+
|
|
295
|
+
if (!dfnId.match(/sec-[a-z]+-objects?/)
|
|
296
|
+
&& !objectsIdsExceptions.includes(dfnId)
|
|
297
|
+
) return;
|
|
298
|
+
const dfn = wrapWithDfn(el);
|
|
299
|
+
// set data-lt
|
|
300
|
+
dfnName = dfnName
|
|
301
|
+
.replace(/^The /, '')
|
|
302
|
+
.replace(/ Objects?$/, '')
|
|
303
|
+
// regexp def includes "(Regular Expression)"
|
|
304
|
+
.replace(/ \([^\)]*\)/, '') ;
|
|
305
|
+
dfn.dataset.lt = dfnName;
|
|
306
|
+
|
|
307
|
+
// FIXME
|
|
308
|
+
// These interfaces are also defined in WebIDL, which in general is
|
|
309
|
+
// the prefered source for these terms
|
|
310
|
+
// Because bikeshed does not yet support spec-specific imports,
|
|
311
|
+
// we hide these terms as not exported
|
|
312
|
+
// cf https://github.com/w3c/reffy/pull/732#issuecomment-925950287
|
|
313
|
+
const exportExceptions = [ "Promise", "DataView", "ArrayBuffer" ];
|
|
314
|
+
if (exportExceptions.includes(dfnName)) {
|
|
315
|
+
dfn.dataset.noexport = "";
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (dfnName.match(/^[A-Z]/)) {
|
|
319
|
+
// set dfn-type
|
|
320
|
+
if (dfnName.match(/Error$/)) {
|
|
321
|
+
dfn.dataset.dfnType = "exception";
|
|
322
|
+
} else if (!el.parentNode.querySelector('[id$="constructor"]')) {
|
|
323
|
+
// Objects without constructors match to the namespace type
|
|
324
|
+
dfn.dataset.dfnType = "namespace";
|
|
325
|
+
} else {
|
|
326
|
+
dfn.dataset.dfnType = "interface";
|
|
327
|
+
}
|
|
328
|
+
// We keep track of types associated with a name
|
|
329
|
+
// to associate the same type to the relevant intrinsic object
|
|
330
|
+
// à la %Math%
|
|
331
|
+
idlTypes[dfnName] = dfn.dataset.dfnType;
|
|
332
|
+
}
|
|
333
|
+
definitionNames.add(dfnName);
|
|
334
|
+
} else if (dfnId.match(/-[a-z]+error$/) && !dfnName.match(/\(/)) {
|
|
335
|
+
const dfn = wrapWithDfn(el);
|
|
336
|
+
dfn.dataset.lt = dfnName;
|
|
337
|
+
dfn.dataset.dfnType = "exception";
|
|
338
|
+
definitionNames.add(dfnName);
|
|
339
|
+
idlTypes[dfnName] = dfn.dataset.dfnType;
|
|
340
|
+
} else if (dfnId.match(/[-\.]prototype[-\.]/)) {
|
|
341
|
+
// methods and attributes on objects
|
|
342
|
+
|
|
343
|
+
// Skip headings with a space and no parenthesis
|
|
344
|
+
// (they mention prototype but aren't a prototype property def)
|
|
345
|
+
// with the exception of "set " and "get " headings
|
|
346
|
+
// (which describe setters and getters)
|
|
347
|
+
if (!dfnName.match(/\(/) && (dfnName.match(/ /) && !dfnName.match(/^[gs]et /))) return;
|
|
348
|
+
|
|
349
|
+
// Skip unscoped internal methods à la [[SetPrototypeOf]](V)
|
|
350
|
+
if (dfnName.match(/\[\[/)) return;
|
|
351
|
+
|
|
352
|
+
// Skip symbol-based property definitions;
|
|
353
|
+
// not clear they're useful as externally referenceable names
|
|
354
|
+
if (dfnName.match(/@@/)) return;
|
|
355
|
+
|
|
356
|
+
// Skip .constructor as that cannot be considered as an attribute
|
|
357
|
+
if (dfnName.match(/\.constructor$/)) return;
|
|
358
|
+
|
|
359
|
+
const dfn = wrapWithDfn(el);
|
|
360
|
+
// set definition scope
|
|
361
|
+
dfn.dataset.dfnFor = dfnName.replace(/\.prototype\..*/, '')
|
|
362
|
+
.replace(/^[gs]et /, ''); // remove "get"/"set" markers
|
|
363
|
+
|
|
364
|
+
// Remove parent object prototype (set as scope)
|
|
365
|
+
dfnName = dfnName.replace(/.*\.prototype\./, '');
|
|
366
|
+
|
|
367
|
+
dfn.dataset.lt = dfnName;
|
|
368
|
+
// set dfn-type
|
|
369
|
+
if (dfn.dataset.lt.match(/\(/)) {
|
|
370
|
+
dfnName = cleanMethodName(dfnName);
|
|
371
|
+
dfn.dataset.lt = dfnName;
|
|
372
|
+
dfn.dataset.dfnType = "method";
|
|
373
|
+
} else {
|
|
374
|
+
dfn.dataset.dfnType = "attribute";
|
|
375
|
+
}
|
|
376
|
+
} else if (el.closest("#sec-value-properties-of-the-global-object")) {
|
|
377
|
+
// properties of the global object
|
|
378
|
+
if (el.id !== "#sec-value-properties-of-the-global-object"){
|
|
379
|
+
const dfn = wrapWithDfn(el);
|
|
380
|
+
dfn.dataset.lt = dfnName;
|
|
381
|
+
dfn.dataset.dfnType = "attribute";
|
|
382
|
+
dfn.dataset.dfnFor = "globalThis";
|
|
383
|
+
}
|
|
384
|
+
} else {
|
|
385
|
+
// We handle other headings that look like a method / property
|
|
386
|
+
// on an object instance (rather than its prototype)
|
|
387
|
+
// or an abstract op
|
|
388
|
+
|
|
389
|
+
// if there is already a dfn element, we move on
|
|
390
|
+
if (el.querySelector("dfn")) return;
|
|
391
|
+
|
|
392
|
+
// only dealing with well-known patterns
|
|
393
|
+
if (!dfnName.match(/^[a-z]+\.[a-z]+/i) // à la JSON.parse
|
|
394
|
+
&& !dfnName.match(/^([a-z]+)+ *\(/i) // à la ArrayCreate ( or decodeURI (
|
|
395
|
+
) return;
|
|
396
|
+
// Skip symbol-based property definitions
|
|
397
|
+
if (dfnName.match(/@@/)) return;
|
|
398
|
+
|
|
399
|
+
// Skip .prototype as that cannot be considered
|
|
400
|
+
// as an attribute
|
|
401
|
+
if (dfnName.match(/\.prototype$/)) return;
|
|
402
|
+
|
|
403
|
+
// Skip headings where foo.bar appears as part of a longer phrase
|
|
404
|
+
if (!dfnName.match(/\(/) && dfnName.match(/ /)) return;
|
|
405
|
+
|
|
406
|
+
// redundant definitions of constructors on the global object
|
|
407
|
+
// e.g. "Array ( . . . )"
|
|
408
|
+
if (dfnName.match(/\. \. \./)) return;
|
|
409
|
+
|
|
410
|
+
const dfn = wrapWithDfn(el);
|
|
411
|
+
|
|
412
|
+
if (dfnName.match(/^[a-z]+\.[a-z]+/i)) {
|
|
413
|
+
// set definition scope
|
|
414
|
+
// This assumes that such methods and attributes are only defined
|
|
415
|
+
// one-level deep from the global scope
|
|
416
|
+
dfn.dataset.dfnFor = dfnName.replace(/\..*$/, '');
|
|
417
|
+
dfnName = dfnName.replace(dfn.dataset.dfnFor + ".", '');
|
|
418
|
+
if (dfnName.match(/\(/)) {
|
|
419
|
+
dfnName = cleanMethodName(dfnName);
|
|
420
|
+
dfn.dataset.lt = dfnName;
|
|
421
|
+
dfn.dataset.dfnType = "method";
|
|
422
|
+
} else {
|
|
423
|
+
dfn.dataset.lt = dfnName;
|
|
424
|
+
if (dfnName.match(/^[A-Z]+$/)) {
|
|
425
|
+
dfn.dataset.dfnType = "const";
|
|
426
|
+
} else {
|
|
427
|
+
dfn.dataset.dfnType = "attribute";
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
} else if (dfnName.match(/^([A-Z]+[a-z]*)+ *\(/)) { // Abstract ops à la ArrayCreate or global constructor
|
|
431
|
+
dfnName = cleanMethodName(dfnName);
|
|
432
|
+
dfn.dataset.lt = dfnName;
|
|
433
|
+
const opName = dfnName.split('(')[0];
|
|
434
|
+
|
|
435
|
+
// distinguish global constructors from abstract operations
|
|
436
|
+
if (idlTypes[opName]) {
|
|
437
|
+
dfn.dataset.dfnType = "constructor";
|
|
438
|
+
dfn.dataset.dfnFor = opName;
|
|
439
|
+
} else {
|
|
440
|
+
// If the name is listed as an Abstract Method
|
|
441
|
+
// we set the dfn-for accordingly
|
|
442
|
+
if (abstractMethods[opName]) {
|
|
443
|
+
dfn.dataset.dfnFor = abstractMethods[opName];
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
dfn.dataset.dfnType = "abstract-op";
|
|
447
|
+
}
|
|
448
|
+
} else { // methods of the global object
|
|
449
|
+
dfnName = cleanMethodName(dfnName);
|
|
450
|
+
dfn.dataset.lt = dfnName;
|
|
451
|
+
dfn.dataset.dfnType = "method";
|
|
452
|
+
dfn.dataset.dfnFor = "globalThis";
|
|
453
|
+
}
|
|
454
|
+
definitionNames.add(dfnName);
|
|
455
|
+
}
|
|
456
|
+
});
|
|
457
|
+
// Extract abstract operations from <emu-eqn> with aoid attribute
|
|
458
|
+
[...document.querySelectorAll("emu-eqn[aoid]")]
|
|
459
|
+
.filter(legacySectionFilter)
|
|
460
|
+
.forEach(el => {
|
|
461
|
+
// Skip definitions of constant values (e.g. msPerDay)
|
|
462
|
+
if (el.textContent.match(/=/)) return;
|
|
463
|
+
const dfn = wrapWithDfn(el);
|
|
464
|
+
dfn.dataset.lt = el.getAttribute("aoid");
|
|
465
|
+
dfn.dataset.dfnType = "abstract-op";
|
|
466
|
+
dfn.id = el.id;
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
// Extract State Components from tables
|
|
470
|
+
[...document.querySelectorAll("figure > table")]
|
|
471
|
+
.filter(legacySectionFilter)
|
|
472
|
+
.forEach(el => {
|
|
473
|
+
const title = el.parentNode.querySelector("figcaption")?.textContent || "";
|
|
474
|
+
if (!title.match(/state components for/i)) return;
|
|
475
|
+
const scope = title.replace(/^.*state components for/i, '').trim();
|
|
476
|
+
for (const td of el.querySelectorAll("tr td:first-child")) {
|
|
477
|
+
const dfn = wrapWithDfn(td);
|
|
478
|
+
dfn.dataset.dfnFor = scope;
|
|
479
|
+
dfn.id = el.closest("emu-table[id],emu-clause[id]").id;
|
|
480
|
+
}
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
[...document.querySelectorAll("dfn")]
|
|
484
|
+
.filter(legacySectionFilter)
|
|
485
|
+
.forEach(el => {
|
|
486
|
+
// Skip definitions in conformance page and conventions page
|
|
487
|
+
if (el.closest('section[data-reffy-page$="conformance.html"]') ||
|
|
488
|
+
el.closest('section[data-reffy-page$="notational-conventions.html"]')) {
|
|
489
|
+
el.removeAttribute("id");
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// rely on the aoid attribute as a hint we're dealing
|
|
494
|
+
// with an abstract-op
|
|
495
|
+
if (el.getAttribute("aoid")) {
|
|
496
|
+
el.dataset.dfnType = "abstract-op";
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// Mark well-known intrinsic objects as the same type as their visible object (if set), defaulting to "interface"
|
|
500
|
+
if (el.textContent.match(/^%[A-Z].*%$/)) {
|
|
501
|
+
el.dataset.dfnType = idlTypes[el.textContent.replace(/%/g, '')] || "interface";
|
|
502
|
+
definitionNames.add(el.textContent.trim());
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// %names% in the global object section are operations of the globalThis object
|
|
506
|
+
if (el.closest('[data-reffy-page$="global-object.html"]') && el.textContent.match(/^%[a-z]+%/i)) {
|
|
507
|
+
el.dataset.dfnFor = "globalThis";
|
|
508
|
+
// TODO: this doesn't capture the arguments
|
|
509
|
+
el.dataset.dfnType = "method";
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// Mark well-known symbols as "const"
|
|
513
|
+
// for lack of a better type, and as the WebIDL spec has been doing
|
|
514
|
+
if (el.textContent.match(/^@@[a-z]*$/i)) {
|
|
515
|
+
el.dataset.dfnType = "const";
|
|
516
|
+
}
|
|
517
|
+
if (el.getAttribute("variants")) {
|
|
518
|
+
el.dataset.lt = (el.dataset.lt ?? el.textContent.trim()) + "|" + el.getAttribute("variants");
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// Skip definitions that have already been identified
|
|
522
|
+
// with a more specific typing
|
|
523
|
+
if (!el.dataset.dfnType) {
|
|
524
|
+
// we already have a matching typed definition
|
|
525
|
+
if (definitionNames.has(el.textContent.trim())) return;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// If the <dfn> has no id, we attach it the one from the closest
|
|
529
|
+
// <emu-clause> with an id
|
|
530
|
+
// Note that this means several definitions can share the same id
|
|
531
|
+
if (!el.getAttribute("id")) {
|
|
532
|
+
if (el.closest("emu-clause[id]")) {
|
|
533
|
+
el.setAttribute("id", el.closest("emu-clause").getAttribute("id"));
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
// Any generic <dfn> not previously filtered out
|
|
538
|
+
// is deemed to be exported, scoped to ECMAScript
|
|
539
|
+
if (!el.dataset.dfnType) {
|
|
540
|
+
if (!el.dataset.dfnFor) {
|
|
541
|
+
el.dataset.dfnFor = "ECMAScript";
|
|
542
|
+
}
|
|
543
|
+
el.dataset.export = "";
|
|
544
|
+
}
|
|
545
|
+
});
|
|
546
|
+
// Another pass of clean up for duplicates
|
|
547
|
+
// This cannot be done in the first pass
|
|
548
|
+
// because %Foo.prototype% does not necessarily get identified before
|
|
549
|
+
// the equivalent " prototype object" dfn
|
|
550
|
+
|
|
551
|
+
[...document.querySelectorAll("dfn[id][data-export]")]
|
|
552
|
+
.filter(legacySectionFilter)
|
|
553
|
+
.forEach(dfn => {
|
|
554
|
+
// we have the syntactic equivalent %x.prototype%
|
|
555
|
+
let m = dfn.textContent.trim().match(/^(.*) prototype( object)?$/);
|
|
556
|
+
if (m && definitionNames.has(`%${m[1].trim()}.prototype%`)) {
|
|
557
|
+
dfn.removeAttribute("id");
|
|
558
|
+
delete dfn.dataset.export;
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
function preProcessHTML() {
|
|
565
|
+
const headingSelector = [
|
|
566
|
+
'h2[id]:not([data-dfn-type]) dfn',
|
|
567
|
+
'h3[id]:not([data-dfn-type]) dfn',
|
|
568
|
+
'h4[id]:not([data-dfn-type]) dfn',
|
|
569
|
+
'h5[id]:not([data-dfn-type]) dfn',
|
|
570
|
+
'h6[id]:not([data-dfn-type]) dfn'
|
|
571
|
+
].join(',');
|
|
572
|
+
|
|
573
|
+
// we copy the id on the dfn when it is set on the surrounding heading
|
|
574
|
+
document.querySelectorAll(headingSelector)
|
|
575
|
+
.forEach(el => {
|
|
576
|
+
const headingId = el.closest("h2, h3, h4, h5, h6").id;
|
|
577
|
+
if (!el.id) {
|
|
578
|
+
el.id = headingId;
|
|
579
|
+
}
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
// all the definitions in indices.html are non-normative, so we skip them
|
|
583
|
+
// to avoid having to properly type them
|
|
584
|
+
// they're not all that interesting
|
|
585
|
+
document.querySelectorAll('section[data-reffy-page$="indices.html"] dfn[id]')
|
|
586
|
+
.forEach(el => {
|
|
587
|
+
el.dataset.dfnSkip = true;
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
document.querySelectorAll("dfn[id]:not([data-dfn-type]):not([data-skip])")
|
|
591
|
+
.forEach(el => {
|
|
592
|
+
// Hard coded rules for special ids
|
|
593
|
+
// dom-style is defined elsewhere
|
|
594
|
+
if (el.id === "dom-style") {
|
|
595
|
+
el.dataset.dfnType = 'attribute';
|
|
596
|
+
el.dataset.dfnFor = 'HTMLElement';
|
|
597
|
+
el.dataset.noexport = "";
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// If there is a link, we assume this documents an imported definition
|
|
602
|
+
// so we make it ignored by removing the id
|
|
603
|
+
if (el.querySelector('a[href^="http"]')) {
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
function preProcessSVG2() {
|
|
610
|
+
const idl = extractWebIdl();
|
|
611
|
+
const idlTree = parse(idl);
|
|
612
|
+
const idlInterfaces = idlTree.filter(item => item.type === "interface" || item.type === "interface mixin");
|
|
613
|
+
|
|
614
|
+
// the only element definition not properly marked up in the SVG spec
|
|
615
|
+
const linkHeading = document.getElementById("LinkElement");
|
|
616
|
+
if (linkHeading && !linkHeading.dataset.dfnType) {
|
|
617
|
+
linkHeading.dataset.dfnType = "element";
|
|
618
|
+
linkHeading.dataset.lt = "link";
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
document.querySelectorAll(".attrdef dfn[id]:not([data-dfn-type]):not([data-skip])")
|
|
622
|
+
.forEach(el => {
|
|
623
|
+
el.dataset.dfnType = "element-attr";
|
|
624
|
+
const attrDesc = document.querySelector('[data-reffy-page$="attindex.html"] th span.attr-name a[href$="#' + el.id + '"]');
|
|
625
|
+
if (attrDesc) {
|
|
626
|
+
el.dataset.dfnFor = attrDesc.closest('tr').querySelector('td').textContent;
|
|
627
|
+
} else {
|
|
628
|
+
console.error("Could not find description for " + el.textContent);
|
|
629
|
+
}
|
|
630
|
+
});
|
|
631
|
+
document.querySelectorAll("dt[id] > .adef, dt[id] > .property")
|
|
632
|
+
.forEach(el => {
|
|
633
|
+
const dt = el.parentNode;
|
|
634
|
+
const newDt = document.createElement("dt");
|
|
635
|
+
const dfn = document.createElement("dfn");
|
|
636
|
+
dfn.id = dt.id;
|
|
637
|
+
dfn.dataset.dfnType = el.classList.contains("adef") ? "element-attr" : "property";
|
|
638
|
+
const indexPage = el.classList.contains("adef") ? "attindex.html" : "propidx.html";
|
|
639
|
+
const attrDesc = document.querySelector('[data-reffy-page$="' + indexPage + '"] th a[href$="#' + dfn.id + '"]');
|
|
640
|
+
if (attrDesc) {
|
|
641
|
+
// TODO: this doesn't deal with grouping of elements, e.g. "text content elements"
|
|
642
|
+
dfn.dataset.dfnFor = [...attrDesc.closest('tr').querySelectorAll('span.element-name a')].map (n => n.textContent).join(',');
|
|
643
|
+
} else {
|
|
644
|
+
console.error("Could not find description for " + el.textContent + "/" + dfn.id);
|
|
645
|
+
}
|
|
646
|
+
dfn.textContent = el.textContent;
|
|
647
|
+
newDt.appendChild(dfn);
|
|
648
|
+
dt.replaceWith(newDt);
|
|
649
|
+
});
|
|
650
|
+
document.querySelectorAll('b[id^="__svg__"]').forEach(el => {
|
|
651
|
+
const [,, containername, membername] = el.id.split('__');
|
|
652
|
+
if (containername && membername) {
|
|
653
|
+
let container = idlTree.find(i => i.name === containername);
|
|
654
|
+
if (container) {
|
|
655
|
+
let member = container.members.find(m => m.name === membername);
|
|
656
|
+
if (member) {
|
|
657
|
+
const dfn = document.createElement("dfn");
|
|
658
|
+
dfn.id = el.id;
|
|
659
|
+
dfn.textContent = el.textContent;
|
|
660
|
+
dfn.dataset.dfnFor = containername;
|
|
661
|
+
dfn.dataset.dfnType = member.type === "operation" ? "method" : member.type;
|
|
662
|
+
el.replaceWith(dfn);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
});
|
|
667
|
+
document.querySelectorAll('h3[id^="Interface"]:not([data-dfn-type])').forEach(el => {
|
|
668
|
+
const name = el.id.slice("Interface".length);
|
|
669
|
+
if (idlTree.find(i => i.name === name && i.type === "interface")) {
|
|
670
|
+
el.dataset.dfnType = "interface";
|
|
671
|
+
el.dataset.lt = name;
|
|
672
|
+
}
|
|
673
|
+
});
|
|
674
|
+
document.querySelectorAll('b[id]:not([data-dfn-type])').forEach(el => {
|
|
675
|
+
const name = el.textContent;
|
|
676
|
+
const idlItem = idlTree.find(i => i.name === name) ;
|
|
677
|
+
if (idlItem) {
|
|
678
|
+
const dfn = document.createElement("dfn");
|
|
679
|
+
dfn.id = el.id;
|
|
680
|
+
dfn.dataset.dfnType = idlItem.type;
|
|
681
|
+
dfn.textContent = el.textContent;
|
|
682
|
+
el.replaceWith(dfn);
|
|
683
|
+
}
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
}
|