@markuplint/ml-core 5.4.0 → 5.6.0
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/CHANGELOG.md +29 -0
- package/lib/ml-core.js +3 -0
- package/lib/ml-dom/helper/accname.d.ts +35 -0
- package/lib/ml-dom/helper/accname.js +69 -17
- package/lib/ml-dom/helper/debug.js +1 -1
- package/lib/ml-dom/helper/literal-values.d.ts +102 -0
- package/lib/ml-dom/helper/literal-values.js +193 -0
- package/lib/ml-dom/helper/pretended-attrs.d.ts +16 -0
- package/lib/ml-dom/helper/pretended-attrs.js +21 -0
- package/lib/ml-dom/helper/slot-assignment.d.ts +48 -0
- package/lib/ml-dom/helper/slot-assignment.js +292 -0
- package/lib/ml-dom/helper/tree-scope.d.ts +130 -0
- package/lib/ml-dom/helper/tree-scope.js +341 -0
- package/lib/ml-dom/helper/walkers.d.ts +15 -0
- package/lib/ml-dom/helper/walkers.js +34 -0
- package/lib/ml-dom/index.d.ts +4 -0
- package/lib/ml-dom/index.js +2 -0
- package/lib/ml-dom/node/attr.d.ts +77 -10
- package/lib/ml-dom/node/attr.js +209 -20
- package/lib/ml-dom/node/block.d.ts +15 -6
- package/lib/ml-dom/node/block.js +17 -8
- package/lib/ml-dom/node/document-fragment.d.ts +18 -6
- package/lib/ml-dom/node/document-fragment.js +19 -7
- package/lib/ml-dom/node/document.d.ts +36 -7
- package/lib/ml-dom/node/document.js +60 -17
- package/lib/ml-dom/node/dom-token-list.js +28 -3
- package/lib/ml-dom/node/element.d.ts +148 -24
- package/lib/ml-dom/node/element.js +258 -48
- package/lib/ml-dom/node/named-node-map.js +2 -1
- package/lib/ml-dom/node/node-list.js +2 -3
- package/lib/ml-dom/node/node.d.ts +55 -10
- package/lib/ml-dom/node/node.js +98 -13
- package/lib/ml-dom/node/parent-node.d.ts +39 -8
- package/lib/ml-dom/node/parent-node.js +79 -12
- package/lib/ml-dom/node/shadow-root.d.ts +162 -0
- package/lib/ml-dom/node/shadow-root.js +219 -0
- package/lib/ml-dom/node/text.d.ts +12 -3
- package/lib/ml-dom/node/text.js +14 -3
- package/lib/ml-rule/ml-rule.js +4 -1
- package/lib/test/index.js +3 -0
- package/lib/utils/index.d.ts +2 -0
- package/lib/utils/index.js +1 -0
- package/lib/utils/search-node-at-location.d.ts +39 -0
- package/lib/utils/search-node-at-location.js +53 -0
- package/package.json +12 -15
|
@@ -149,9 +149,14 @@ export declare abstract class MLNode<T extends RuleConfigValue, O extends PlainD
|
|
|
149
149
|
*/
|
|
150
150
|
get baseURI(): string;
|
|
151
151
|
/**
|
|
152
|
-
* The list of child nodes
|
|
153
|
-
*
|
|
154
|
-
*
|
|
152
|
+
* The list of child nodes. Fragment nodes and preprocessor blocks whose
|
|
153
|
+
* `blockBehavior.type` is `each` or `end` are flattened into this list.
|
|
154
|
+
* Conditional blocks (`if`, `switch`, `await`, and their clauses) and
|
|
155
|
+
* `each:empty` stay in this list: flattening a conditional block would
|
|
156
|
+
* present elements that never render together as siblings, and would
|
|
157
|
+
* remove the block nodes `conditionalChildNodes()` uses to enumerate
|
|
158
|
+
* each branch. A loop body is not an alternative branch, so `each` and
|
|
159
|
+
* `end` are flattened.
|
|
155
160
|
*
|
|
156
161
|
* @readonly
|
|
157
162
|
* @implements DOM API: `Node`
|
|
@@ -244,9 +249,30 @@ export declare abstract class MLNode<T extends RuleConfigValue, O extends PlainD
|
|
|
244
249
|
* @see https://dom.spec.whatwg.org/#ref-for-dom-node-parentelement%E2%91%A0
|
|
245
250
|
*/
|
|
246
251
|
get parentElement(): MLElement<T, O> | null;
|
|
252
|
+
/**
|
|
253
|
+
* The parent element in the tree of this node, which is what the DOM calls `parentElement`: `null`
|
|
254
|
+
* for what is at the top of the content of an HTML `template` element or of a declarative shadow
|
|
255
|
+
* root, whose {@link MLNode.parentElement} is the `template` element, as the syntax tree has it.
|
|
256
|
+
* A `template` element that is in no tree has none either.
|
|
257
|
+
*
|
|
258
|
+
* The selector engine and `closest()` read it, so that `section p` does not match a `p` in the
|
|
259
|
+
* content of a `template` in a `section`. {@link MLNode.parentElement} does not stop at that
|
|
260
|
+
* boundary because the content model of a `template` element is validated against what is in it.
|
|
261
|
+
*
|
|
262
|
+
* `getTreeParentElement` of `@markuplint/ml-spec`, which cannot use this getter, derives the same
|
|
263
|
+
* answer from `getRootNode()` for the algorithms of that package: a change of the boundary here
|
|
264
|
+
* belongs to it too.
|
|
265
|
+
*
|
|
266
|
+
* @implements `@markuplint/ml-core` API: `MLNode`
|
|
267
|
+
*/
|
|
268
|
+
get treeParentElement(): MLElement<T, O> | null;
|
|
247
269
|
/**
|
|
248
270
|
* The parent node that may be `Element`, `Document`, `DocumentFragment`, and `null`.
|
|
249
271
|
*
|
|
272
|
+
* A block of a template language is walked through (`MLBlock#isTransparent`): a node in a block
|
|
273
|
+
* has the node that contains the block as its parent. A node at the top of a fragment document
|
|
274
|
+
* has none.
|
|
275
|
+
*
|
|
250
276
|
* ## HTML:
|
|
251
277
|
*
|
|
252
278
|
* ```html
|
|
@@ -267,10 +293,10 @@ export declare abstract class MLNode<T extends RuleConfigValue, O extends PlainD
|
|
|
267
293
|
*
|
|
268
294
|
* ```jsx
|
|
269
295
|
* <> // => null
|
|
270
|
-
* <div> // =>
|
|
296
|
+
* <div> // => <>
|
|
271
297
|
* {items.map(item => {
|
|
272
298
|
* return (
|
|
273
|
-
* <span /> // =>
|
|
299
|
+
* <span /> // => <div>
|
|
274
300
|
* )
|
|
275
301
|
* })}
|
|
276
302
|
* </div>
|
|
@@ -284,7 +310,7 @@ export declare abstract class MLNode<T extends RuleConfigValue, O extends PlainD
|
|
|
284
310
|
* div
|
|
285
311
|
* //- <div>
|
|
286
312
|
* if foo
|
|
287
|
-
* //-
|
|
313
|
+
* //- <div>
|
|
288
314
|
* span
|
|
289
315
|
* ```
|
|
290
316
|
*
|
|
@@ -334,7 +360,7 @@ export declare abstract class MLNode<T extends RuleConfigValue, O extends PlainD
|
|
|
334
360
|
*
|
|
335
361
|
* ```jsx
|
|
336
362
|
* <> // => #document
|
|
337
|
-
* <div> // =>
|
|
363
|
+
* <div> // => <>
|
|
338
364
|
* {items.map(item => {
|
|
339
365
|
* return (
|
|
340
366
|
* <span /> // => #ml-block
|
|
@@ -425,6 +451,13 @@ export declare abstract class MLNode<T extends RuleConfigValue, O extends PlainD
|
|
|
425
451
|
/**
|
|
426
452
|
* Returns true if other is an inclusive descendant of node; otherwise false.
|
|
427
453
|
*
|
|
454
|
+
* What an HTML `template` element holds is its `content`, a tree of its own, so the element does
|
|
455
|
+
* not contain it, and an element does not contain the content of a `template` under it, so
|
|
456
|
+
* `contains()` asked of what is outside a template about what is inside it is `false` as well.
|
|
457
|
+
* That differs from {@link MLNode.parentElement}, which stays the one of the syntax
|
|
458
|
+
* tree (the `template` element) because the content model of a `template` element is validated
|
|
459
|
+
* against what the syntax tree puts in it.
|
|
460
|
+
*
|
|
428
461
|
* @implements DOM API: `Node`
|
|
429
462
|
* @see https://dom.spec.whatwg.org/#ref-for-dom-node-contains%E2%91%A0
|
|
430
463
|
*/
|
|
@@ -438,7 +471,9 @@ export declare abstract class MLNode<T extends RuleConfigValue, O extends PlainD
|
|
|
438
471
|
*/
|
|
439
472
|
dispatchEvent(event: Event): boolean;
|
|
440
473
|
/**
|
|
441
|
-
* Finds subsequent nodes that match the given selector
|
|
474
|
+
* Finds subsequent nodes that match the given selector, in the tree of this node: what follows in
|
|
475
|
+
* the markup but is in another tree (the content of an HTML `template` element) is not subsequent
|
|
476
|
+
* to it.
|
|
442
477
|
*
|
|
443
478
|
* @implements `@markuplint/ml-core` API: `MLNode`
|
|
444
479
|
* @param selector - Optional selector to filter the nodes.
|
|
@@ -451,9 +486,16 @@ export declare abstract class MLNode<T extends RuleConfigValue, O extends PlainD
|
|
|
451
486
|
*/
|
|
452
487
|
getPureChildNodes(): NodeListOf<MLChildNode<T, O>>;
|
|
453
488
|
/**
|
|
454
|
-
*
|
|
489
|
+
* The root of the tree of this node: the document, the content of the nearest HTML `template`
|
|
490
|
+
* element above it (a declarative shadow root when one is attached to the parent of the `template`),
|
|
491
|
+
* or this node itself for a document fragment, and for a `template` element that is in no tree.
|
|
492
|
+
* The syntax tree is followed through what a template engine renders away, so a block of a
|
|
493
|
+
* template language between the `template` element and this node does not hide the `template`.
|
|
455
494
|
*
|
|
456
|
-
*
|
|
495
|
+
* With `composed`, a shadow root is followed to its host.
|
|
496
|
+
*
|
|
497
|
+
* @param options - `composed` follows a shadow root to its host
|
|
498
|
+
* @returns The root of the tree
|
|
457
499
|
* @implements DOM API: `Node`
|
|
458
500
|
* @see https://dom.spec.whatwg.org/#ref-for-dom-node-getrootnode%E2%91%A0
|
|
459
501
|
*/
|
|
@@ -502,6 +544,9 @@ export declare abstract class MLNode<T extends RuleConfigValue, O extends PlainD
|
|
|
502
544
|
*/
|
|
503
545
|
isEqualNode(otherNode: Node | null): boolean;
|
|
504
546
|
/**
|
|
547
|
+
* Whether the document of this node is a fragment of a document (a partial, a component) and
|
|
548
|
+
* not a whole one. The parser of the document decides it (`MLDocument#isFragment`).
|
|
549
|
+
*
|
|
505
550
|
* @implements `@markuplint/ml-core` API: `MLNode`
|
|
506
551
|
*/
|
|
507
552
|
isInFragmentDocument(): boolean;
|
package/lib/ml-dom/node/node.js
CHANGED
|
@@ -174,9 +174,14 @@ export class MLNode extends MLToken {
|
|
|
174
174
|
throw new UnexpectedCallError('Not supported "baseURI" property');
|
|
175
175
|
}
|
|
176
176
|
/**
|
|
177
|
-
* The list of child nodes
|
|
178
|
-
*
|
|
179
|
-
*
|
|
177
|
+
* The list of child nodes. Fragment nodes and preprocessor blocks whose
|
|
178
|
+
* `blockBehavior.type` is `each` or `end` are flattened into this list.
|
|
179
|
+
* Conditional blocks (`if`, `switch`, `await`, and their clauses) and
|
|
180
|
+
* `each:empty` stay in this list: flattening a conditional block would
|
|
181
|
+
* present elements that never render together as siblings, and would
|
|
182
|
+
* remove the block nodes `conditionalChildNodes()` uses to enumerate
|
|
183
|
+
* each branch. A loop body is not an alternative branch, so `each` and
|
|
184
|
+
* `end` are flattened.
|
|
180
185
|
*
|
|
181
186
|
* @readonly
|
|
182
187
|
* @implements DOM API: `Node`
|
|
@@ -321,9 +326,39 @@ export class MLNode extends MLToken {
|
|
|
321
326
|
}
|
|
322
327
|
return null;
|
|
323
328
|
}
|
|
329
|
+
/**
|
|
330
|
+
* The parent element in the tree of this node, which is what the DOM calls `parentElement`: `null`
|
|
331
|
+
* for what is at the top of the content of an HTML `template` element or of a declarative shadow
|
|
332
|
+
* root, whose {@link MLNode.parentElement} is the `template` element, as the syntax tree has it.
|
|
333
|
+
* A `template` element that is in no tree has none either.
|
|
334
|
+
*
|
|
335
|
+
* The selector engine and `closest()` read it, so that `section p` does not match a `p` in the
|
|
336
|
+
* content of a `template` in a `section`. {@link MLNode.parentElement} does not stop at that
|
|
337
|
+
* boundary because the content model of a `template` element is validated against what is in it.
|
|
338
|
+
*
|
|
339
|
+
* `getTreeParentElement` of `@markuplint/ml-spec`, which cannot use this getter, derives the same
|
|
340
|
+
* answer from `getRootNode()` for the algorithms of that package: a change of the boundary here
|
|
341
|
+
* belongs to it too.
|
|
342
|
+
*
|
|
343
|
+
* @implements `@markuplint/ml-core` API: `MLNode`
|
|
344
|
+
*/
|
|
345
|
+
get treeParentElement() {
|
|
346
|
+
const parent = this.parentElement;
|
|
347
|
+
if (!parent || parent.isHTMLTemplateElement) {
|
|
348
|
+
return null;
|
|
349
|
+
}
|
|
350
|
+
if (this.is(this.ELEMENT_NODE) && this.isInNoTree) {
|
|
351
|
+
return null;
|
|
352
|
+
}
|
|
353
|
+
return parent;
|
|
354
|
+
}
|
|
324
355
|
/**
|
|
325
356
|
* The parent node that may be `Element`, `Document`, `DocumentFragment`, and `null`.
|
|
326
357
|
*
|
|
358
|
+
* A block of a template language is walked through (`MLBlock#isTransparent`): a node in a block
|
|
359
|
+
* has the node that contains the block as its parent. A node at the top of a fragment document
|
|
360
|
+
* has none.
|
|
361
|
+
*
|
|
327
362
|
* ## HTML:
|
|
328
363
|
*
|
|
329
364
|
* ```html
|
|
@@ -344,10 +379,10 @@ export class MLNode extends MLToken {
|
|
|
344
379
|
*
|
|
345
380
|
* ```jsx
|
|
346
381
|
* <> // => null
|
|
347
|
-
* <div> // =>
|
|
382
|
+
* <div> // => <>
|
|
348
383
|
* {items.map(item => {
|
|
349
384
|
* return (
|
|
350
|
-
* <span /> // =>
|
|
385
|
+
* <span /> // => <div>
|
|
351
386
|
* )
|
|
352
387
|
* })}
|
|
353
388
|
* </div>
|
|
@@ -361,7 +396,7 @@ export class MLNode extends MLToken {
|
|
|
361
396
|
* div
|
|
362
397
|
* //- <div>
|
|
363
398
|
* if foo
|
|
364
|
-
* //-
|
|
399
|
+
* //- <div>
|
|
365
400
|
* span
|
|
366
401
|
* ```
|
|
367
402
|
*
|
|
@@ -480,7 +515,7 @@ export class MLNode extends MLToken {
|
|
|
480
515
|
*
|
|
481
516
|
* ```jsx
|
|
482
517
|
* <> // => #document
|
|
483
|
-
* <div> // =>
|
|
518
|
+
* <div> // => <>
|
|
484
519
|
* {items.map(item => {
|
|
485
520
|
* return (
|
|
486
521
|
* <span /> // => #ml-block
|
|
@@ -655,12 +690,22 @@ export class MLNode extends MLToken {
|
|
|
655
690
|
/**
|
|
656
691
|
* Returns true if other is an inclusive descendant of node; otherwise false.
|
|
657
692
|
*
|
|
693
|
+
* What an HTML `template` element holds is its `content`, a tree of its own, so the element does
|
|
694
|
+
* not contain it, and an element does not contain the content of a `template` under it, so
|
|
695
|
+
* `contains()` asked of what is outside a template about what is inside it is `false` as well.
|
|
696
|
+
* That differs from {@link MLNode.parentElement}, which stays the one of the syntax
|
|
697
|
+
* tree (the `template` element) because the content model of a `template` element is validated
|
|
698
|
+
* against what the syntax tree puts in it.
|
|
699
|
+
*
|
|
658
700
|
* @implements DOM API: `Node`
|
|
659
701
|
* @see https://dom.spec.whatwg.org/#ref-for-dom-node-contains%E2%91%A0
|
|
660
702
|
*/
|
|
661
703
|
contains(
|
|
662
704
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
663
705
|
other) {
|
|
706
|
+
if (this.is(this.ELEMENT_NODE) && this.isHTMLTemplateElement) {
|
|
707
|
+
return false;
|
|
708
|
+
}
|
|
664
709
|
for (const childNode of this.childNodes) {
|
|
665
710
|
if (other?.uuid === childNode.uuid || childNode.contains(other)) {
|
|
666
711
|
return true;
|
|
@@ -681,7 +726,9 @@ export class MLNode extends MLToken {
|
|
|
681
726
|
throw new UnexpectedCallError('Not supported "dispatchEvent" method');
|
|
682
727
|
}
|
|
683
728
|
/**
|
|
684
|
-
* Finds subsequent nodes that match the given selector
|
|
729
|
+
* Finds subsequent nodes that match the given selector, in the tree of this node: what follows in
|
|
730
|
+
* the markup but is in another tree (the content of an HTML `template` element) is not subsequent
|
|
731
|
+
* to it.
|
|
685
732
|
*
|
|
686
733
|
* @implements `@markuplint/ml-core` API: `MLNode`
|
|
687
734
|
* @param selector - Optional selector to filter the nodes.
|
|
@@ -689,6 +736,7 @@ export class MLNode extends MLToken {
|
|
|
689
736
|
*/
|
|
690
737
|
findSubsequentNodes(selector) {
|
|
691
738
|
const matched = [];
|
|
739
|
+
const root = this.getRootNode();
|
|
692
740
|
for (const node of this.ownerMLDocument.nodeList) {
|
|
693
741
|
if (node.endOffset <= this.endOffset) {
|
|
694
742
|
continue;
|
|
@@ -696,6 +744,9 @@ export class MLNode extends MLToken {
|
|
|
696
744
|
if (this.contains(node)) {
|
|
697
745
|
continue;
|
|
698
746
|
}
|
|
747
|
+
if (node.getRootNode() !== root) {
|
|
748
|
+
continue;
|
|
749
|
+
}
|
|
699
750
|
if (selector) {
|
|
700
751
|
if (node.is(node.ELEMENT_NODE) && node.matches(selector)) {
|
|
701
752
|
matched.push(node);
|
|
@@ -750,19 +801,50 @@ export class MLNode extends MLToken {
|
|
|
750
801
|
return this.#pureChildNodesCache;
|
|
751
802
|
}
|
|
752
803
|
/**
|
|
753
|
-
*
|
|
804
|
+
* The root of the tree of this node: the document, the content of the nearest HTML `template`
|
|
805
|
+
* element above it (a declarative shadow root when one is attached to the parent of the `template`),
|
|
806
|
+
* or this node itself for a document fragment, and for a `template` element that is in no tree.
|
|
807
|
+
* The syntax tree is followed through what a template engine renders away, so a block of a
|
|
808
|
+
* template language between the `template` element and this node does not hide the `template`.
|
|
754
809
|
*
|
|
755
|
-
*
|
|
810
|
+
* With `composed`, a shadow root is followed to its host.
|
|
811
|
+
*
|
|
812
|
+
* @param options - `composed` follows a shadow root to its host
|
|
813
|
+
* @returns The root of the tree
|
|
756
814
|
* @implements DOM API: `Node`
|
|
757
815
|
* @see https://dom.spec.whatwg.org/#ref-for-dom-node-getrootnode%E2%91%A0
|
|
758
816
|
*/
|
|
759
817
|
getRootNode(
|
|
760
818
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
761
819
|
options) {
|
|
762
|
-
|
|
763
|
-
|
|
820
|
+
const root = this.#getTreeRoot();
|
|
821
|
+
if (options?.composed && root.is(root.DOCUMENT_FRAGMENT_NODE) && 'host' in root) {
|
|
822
|
+
return root.host.getRootNode(options);
|
|
823
|
+
}
|
|
824
|
+
return root;
|
|
825
|
+
}
|
|
826
|
+
#treeRoot;
|
|
827
|
+
#getTreeRoot() {
|
|
828
|
+
if (this.#treeRoot) {
|
|
829
|
+
return this.#treeRoot;
|
|
830
|
+
}
|
|
831
|
+
this.#treeRoot = this.#findTreeRoot();
|
|
832
|
+
return this.#treeRoot;
|
|
833
|
+
}
|
|
834
|
+
#findTreeRoot() {
|
|
835
|
+
if (this.is(this.DOCUMENT_NODE) || this.is(this.DOCUMENT_FRAGMENT_NODE)) {
|
|
836
|
+
return this;
|
|
837
|
+
}
|
|
838
|
+
if (this.is(this.ELEMENT_NODE) && this.isInNoTree) {
|
|
839
|
+
return this;
|
|
840
|
+
}
|
|
841
|
+
let ancestor = this.syntacticalParentNode;
|
|
842
|
+
while (ancestor && !ancestor.is(ancestor.DOCUMENT_NODE)) {
|
|
843
|
+
if (ancestor.is(ancestor.ELEMENT_NODE) && ancestor.isHTMLTemplateElement) {
|
|
844
|
+
return ancestor.content ?? this.#ownerDocument;
|
|
845
|
+
}
|
|
846
|
+
ancestor = ancestor.syntacticalParentNode;
|
|
764
847
|
}
|
|
765
|
-
// The original DOM API returns a document fragment if the element is a fragment.
|
|
766
848
|
return this.#ownerDocument;
|
|
767
849
|
}
|
|
768
850
|
/**
|
|
@@ -829,6 +911,9 @@ export class MLNode extends MLToken {
|
|
|
829
911
|
throw new UnexpectedCallError('Not supported "isEqualNode" method');
|
|
830
912
|
}
|
|
831
913
|
/**
|
|
914
|
+
* Whether the document of this node is a fragment of a document (a partial, a component) and
|
|
915
|
+
* not a whole one. The parser of the document decides it (`MLDocument#isFragment`).
|
|
916
|
+
*
|
|
832
917
|
* @implements `@markuplint/ml-core` API: `MLNode`
|
|
833
918
|
*/
|
|
834
919
|
isInFragmentDocument() {
|
|
@@ -15,17 +15,17 @@ export declare abstract class MLParentNode<T extends RuleConfigValue, O extends
|
|
|
15
15
|
get childElementCount(): number;
|
|
16
16
|
/**
|
|
17
17
|
* @implements DOM API: `Element`, `Document`, `DocumentFragment`
|
|
18
|
-
* @see https://dom.spec.whatwg.org/#
|
|
18
|
+
* @see https://dom.spec.whatwg.org/#dom-parentnode-children
|
|
19
19
|
*/
|
|
20
20
|
get children(): HTMLCollectionOf<MLElement<T, O>>;
|
|
21
21
|
/**
|
|
22
22
|
* @implements DOM API: `Element`, `Document`, `DocumentFragment`
|
|
23
|
-
* @see https://dom.spec.whatwg.org/#
|
|
23
|
+
* @see https://dom.spec.whatwg.org/#dom-parentnode-firstelementchild
|
|
24
24
|
*/
|
|
25
25
|
get firstElementChild(): MLElement<T, O> | null;
|
|
26
26
|
/**
|
|
27
27
|
* @implements DOM API: `Element`, `Document`, `DocumentFragment`
|
|
28
|
-
* @see https://dom.spec.whatwg.org/#
|
|
28
|
+
* @see https://dom.spec.whatwg.org/#dom-parentnode-lastelementchild
|
|
29
29
|
*/
|
|
30
30
|
get lastElementChild(): MLElement<T, O> | null;
|
|
31
31
|
/**
|
|
@@ -33,7 +33,7 @@ export declare abstract class MLParentNode<T extends RuleConfigValue, O extends
|
|
|
33
33
|
*
|
|
34
34
|
* @unsupported
|
|
35
35
|
* @implements DOM API: `Element`, `Document`, `DocumentFragment`
|
|
36
|
-
* @see https://dom.spec.whatwg.org/#
|
|
36
|
+
* @see https://dom.spec.whatwg.org/#dom-parentnode-append
|
|
37
37
|
*/
|
|
38
38
|
append(...nodes: (string | Node)[]): void;
|
|
39
39
|
/**
|
|
@@ -41,26 +41,57 @@ export declare abstract class MLParentNode<T extends RuleConfigValue, O extends
|
|
|
41
41
|
*
|
|
42
42
|
* @unsupported
|
|
43
43
|
* @implements DOM API: `Element`, `Document`, `DocumentFragment`
|
|
44
|
-
* @see https://dom.spec.whatwg.org/#
|
|
44
|
+
* @see https://dom.spec.whatwg.org/#dom-parentnode-prepend
|
|
45
45
|
*/
|
|
46
46
|
prepend(...nodes: (string | Node)[]): void;
|
|
47
47
|
/**
|
|
48
48
|
* @implements DOM API: `Element`, `Document`, `DocumentFragment`
|
|
49
|
-
* @see https://dom.spec.whatwg.org/#
|
|
49
|
+
* @see https://dom.spec.whatwg.org/#dom-parentnode-queryselector
|
|
50
50
|
*/
|
|
51
51
|
querySelector(selectors: string): MLElement<T, O> | null;
|
|
52
52
|
/**
|
|
53
|
+
* Selects the descendants in the tree order, within the tree of this node: not the elements in
|
|
54
|
+
* the content of an HTML `template` element, which belong to a tree of their own.
|
|
55
|
+
*
|
|
53
56
|
* @implements DOM API: `Element`, `Document`, `DocumentFragment`
|
|
54
|
-
* @see https://dom.spec.whatwg.org/#
|
|
57
|
+
* @see https://dom.spec.whatwg.org/#dom-parentnode-queryselectorall
|
|
55
58
|
*/
|
|
56
59
|
querySelectorAll(selectors: string): NodeListOf<MLElement<T, O>>;
|
|
60
|
+
/**
|
|
61
|
+
* Selects the descendants in every tree under this node: the content of an HTML `template`
|
|
62
|
+
* element (and a declarative shadow root) at any depth as well, and a `template` element that is in
|
|
63
|
+
* no tree. A selector is matched in the tree of each element, so `section p` does not match a `p`
|
|
64
|
+
* in the content of a `template` that is in a `section`.
|
|
65
|
+
*
|
|
66
|
+
* It is for a rule that must look at every element whatever tree it is in (a restricted element,
|
|
67
|
+
* a table, a line break), where `querySelectorAll` would leave the content of a `template`
|
|
68
|
+
* unchecked.
|
|
69
|
+
*
|
|
70
|
+
* @implements `@markuplint/ml-core` API: `MLParentNode`
|
|
71
|
+
* @param selectors - A selector
|
|
72
|
+
* @returns The matched elements in the order of the markup
|
|
73
|
+
*/
|
|
74
|
+
deepQuerySelectorAll(selectors: string): readonly MLElement<T, O>[];
|
|
75
|
+
/**
|
|
76
|
+
* The first element of the tree whose `id` is `elementId` (the string that the DOM has, with
|
|
77
|
+
* character references resolved), found through a map that is built once: a rule resolves one
|
|
78
|
+
* reference per attribute.
|
|
79
|
+
*
|
|
80
|
+
* @param elementId - The id
|
|
81
|
+
* @returns The element, or `null`
|
|
82
|
+
*/
|
|
83
|
+
protected _getElementById(elementId: string): MLElement<T, O> | null;
|
|
57
84
|
/**
|
|
58
85
|
* **IT THROWS AN ERROR WHEN CALLING THIS.**
|
|
59
86
|
*
|
|
60
87
|
* @unsupported
|
|
61
88
|
* @implements DOM API: `Element`, `Document`, `DocumentFragment`
|
|
62
|
-
* @see https://dom.spec.whatwg.org/#
|
|
89
|
+
* @see https://dom.spec.whatwg.org/#dom-parentnode-replacechildren
|
|
63
90
|
*/
|
|
64
91
|
replaceChildren(...nodes: (string | Node)[]): void;
|
|
92
|
+
/**
|
|
93
|
+
* The descendants in the tree order, within the tree of this node (see {@link walkTree}). An
|
|
94
|
+
* HTML `template` element has none: what it holds is its `content`, a tree of its own.
|
|
95
|
+
*/
|
|
65
96
|
protected _descendantsToArray<N extends MLNode<T, O>>(filter?: (node: MLNode<T, O>) => N | null | void): ReadonlyArray<N>;
|
|
66
97
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getDOMValue } from '../helper/tree-scope.js';
|
|
2
|
+
import { walkTree } from '../helper/walkers.js';
|
|
2
3
|
import { getChildren } from '../manipulations/get-children.js';
|
|
3
4
|
import { toNodeList } from './node-list.js';
|
|
4
5
|
import { MLNode } from './node.js';
|
|
@@ -10,6 +11,8 @@ import { UnexpectedCallError } from '@markuplint/shared';
|
|
|
10
11
|
export class MLParentNode extends MLNode {
|
|
11
12
|
#children = null;
|
|
12
13
|
#selectedElements = new Map();
|
|
14
|
+
#deepSelectedElements = new Map();
|
|
15
|
+
#idMap = null;
|
|
13
16
|
/**
|
|
14
17
|
* @implements DOM API: `Element`, `Document`, `DocumentFragment`
|
|
15
18
|
* @see https://dom.spec.whatwg.org/#dom-parentnode-childelementcount
|
|
@@ -19,7 +22,7 @@ export class MLParentNode extends MLNode {
|
|
|
19
22
|
}
|
|
20
23
|
/**
|
|
21
24
|
* @implements DOM API: `Element`, `Document`, `DocumentFragment`
|
|
22
|
-
* @see https://dom.spec.whatwg.org/#
|
|
25
|
+
* @see https://dom.spec.whatwg.org/#dom-parentnode-children
|
|
23
26
|
*/
|
|
24
27
|
get children() {
|
|
25
28
|
if (this.#children) {
|
|
@@ -31,24 +34,24 @@ export class MLParentNode extends MLNode {
|
|
|
31
34
|
}
|
|
32
35
|
/**
|
|
33
36
|
* @implements DOM API: `Element`, `Document`, `DocumentFragment`
|
|
34
|
-
* @see https://dom.spec.whatwg.org/#
|
|
37
|
+
* @see https://dom.spec.whatwg.org/#dom-parentnode-firstelementchild
|
|
35
38
|
*/
|
|
36
39
|
get firstElementChild() {
|
|
37
|
-
return this.children
|
|
40
|
+
return this.children.item(0);
|
|
38
41
|
}
|
|
39
42
|
/**
|
|
40
43
|
* @implements DOM API: `Element`, `Document`, `DocumentFragment`
|
|
41
|
-
* @see https://dom.spec.whatwg.org/#
|
|
44
|
+
* @see https://dom.spec.whatwg.org/#dom-parentnode-lastelementchild
|
|
42
45
|
*/
|
|
43
46
|
get lastElementChild() {
|
|
44
|
-
return this.children
|
|
47
|
+
return this.children.item(this.children.length - 1);
|
|
45
48
|
}
|
|
46
49
|
/**
|
|
47
50
|
* **IT THROWS AN ERROR WHEN CALLING THIS.**
|
|
48
51
|
*
|
|
49
52
|
* @unsupported
|
|
50
53
|
* @implements DOM API: `Element`, `Document`, `DocumentFragment`
|
|
51
|
-
* @see https://dom.spec.whatwg.org/#
|
|
54
|
+
* @see https://dom.spec.whatwg.org/#dom-parentnode-append
|
|
52
55
|
*/
|
|
53
56
|
append(
|
|
54
57
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
@@ -60,7 +63,7 @@ export class MLParentNode extends MLNode {
|
|
|
60
63
|
*
|
|
61
64
|
* @unsupported
|
|
62
65
|
* @implements DOM API: `Element`, `Document`, `DocumentFragment`
|
|
63
|
-
* @see https://dom.spec.whatwg.org/#
|
|
66
|
+
* @see https://dom.spec.whatwg.org/#dom-parentnode-prepend
|
|
64
67
|
*/
|
|
65
68
|
prepend(
|
|
66
69
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
@@ -69,15 +72,18 @@ export class MLParentNode extends MLNode {
|
|
|
69
72
|
}
|
|
70
73
|
/**
|
|
71
74
|
* @implements DOM API: `Element`, `Document`, `DocumentFragment`
|
|
72
|
-
* @see https://dom.spec.whatwg.org/#
|
|
75
|
+
* @see https://dom.spec.whatwg.org/#dom-parentnode-queryselector
|
|
73
76
|
*/
|
|
74
77
|
querySelector(selectors) {
|
|
75
78
|
const selected = this.querySelectorAll(selectors);
|
|
76
79
|
return selected[0] ?? null;
|
|
77
80
|
}
|
|
78
81
|
/**
|
|
82
|
+
* Selects the descendants in the tree order, within the tree of this node: not the elements in
|
|
83
|
+
* the content of an HTML `template` element, which belong to a tree of their own.
|
|
84
|
+
*
|
|
79
85
|
* @implements DOM API: `Element`, `Document`, `DocumentFragment`
|
|
80
|
-
* @see https://dom.spec.whatwg.org/#
|
|
86
|
+
* @see https://dom.spec.whatwg.org/#dom-parentnode-queryselectorall
|
|
81
87
|
*/
|
|
82
88
|
querySelectorAll(selectors) {
|
|
83
89
|
const selected = this.#selectedElements.get(selectors);
|
|
@@ -92,21 +98,82 @@ export class MLParentNode extends MLNode {
|
|
|
92
98
|
this.#selectedElements.set(selectors, elements);
|
|
93
99
|
return elements;
|
|
94
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* Selects the descendants in every tree under this node: the content of an HTML `template`
|
|
103
|
+
* element (and a declarative shadow root) at any depth as well, and a `template` element that is in
|
|
104
|
+
* no tree. A selector is matched in the tree of each element, so `section p` does not match a `p`
|
|
105
|
+
* in the content of a `template` that is in a `section`.
|
|
106
|
+
*
|
|
107
|
+
* It is for a rule that must look at every element whatever tree it is in (a restricted element,
|
|
108
|
+
* a table, a line break), where `querySelectorAll` would leave the content of a `template`
|
|
109
|
+
* unchecked.
|
|
110
|
+
*
|
|
111
|
+
* @implements `@markuplint/ml-core` API: `MLParentNode`
|
|
112
|
+
* @param selectors - A selector
|
|
113
|
+
* @returns The matched elements in the order of the markup
|
|
114
|
+
*/
|
|
115
|
+
deepQuerySelectorAll(selectors) {
|
|
116
|
+
const selected = this.#deepSelectedElements.get(selectors);
|
|
117
|
+
if (selected) {
|
|
118
|
+
return selected;
|
|
119
|
+
}
|
|
120
|
+
const elements = [];
|
|
121
|
+
walkTree([...this.childNodes], node => {
|
|
122
|
+
if (node.is(node.ELEMENT_NODE) && node.matches(selectors, this)) {
|
|
123
|
+
elements.push(node);
|
|
124
|
+
}
|
|
125
|
+
}, true);
|
|
126
|
+
const result = Object.freeze(elements);
|
|
127
|
+
this.#deepSelectedElements.set(selectors, result);
|
|
128
|
+
return result;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* The first element of the tree whose `id` is `elementId` (the string that the DOM has, with
|
|
132
|
+
* character references resolved), found through a map that is built once: a rule resolves one
|
|
133
|
+
* reference per attribute.
|
|
134
|
+
*
|
|
135
|
+
* @param elementId - The id
|
|
136
|
+
* @returns The element, or `null`
|
|
137
|
+
*/
|
|
138
|
+
_getElementById(elementId) {
|
|
139
|
+
if (!this.#idMap) {
|
|
140
|
+
const idMap = new Map();
|
|
141
|
+
for (const el of this._descendantsToArray(node => {
|
|
142
|
+
if (node.is(node.ELEMENT_NODE)) {
|
|
143
|
+
return node;
|
|
144
|
+
}
|
|
145
|
+
})) {
|
|
146
|
+
const id = getDOMValue(el.getAttributeNode('id'));
|
|
147
|
+
if (id != null && id !== '' && !idMap.has(id)) {
|
|
148
|
+
idMap.set(id, el);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
this.#idMap = idMap;
|
|
152
|
+
}
|
|
153
|
+
return this.#idMap.get(elementId) ?? null;
|
|
154
|
+
}
|
|
95
155
|
/**
|
|
96
156
|
* **IT THROWS AN ERROR WHEN CALLING THIS.**
|
|
97
157
|
*
|
|
98
158
|
* @unsupported
|
|
99
159
|
* @implements DOM API: `Element`, `Document`, `DocumentFragment`
|
|
100
|
-
* @see https://dom.spec.whatwg.org/#
|
|
160
|
+
* @see https://dom.spec.whatwg.org/#dom-parentnode-replacechildren
|
|
101
161
|
*/
|
|
102
162
|
replaceChildren(
|
|
103
163
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
104
164
|
...nodes) {
|
|
105
165
|
throw new UnexpectedCallError('Not supported "replaceChildren" method');
|
|
106
166
|
}
|
|
167
|
+
/**
|
|
168
|
+
* The descendants in the tree order, within the tree of this node (see {@link walkTree}). An
|
|
169
|
+
* HTML `template` element has none: what it holds is its `content`, a tree of its own.
|
|
170
|
+
*/
|
|
107
171
|
_descendantsToArray(filter) {
|
|
172
|
+
if (this.is(this.ELEMENT_NODE) && this.isHTMLTemplateElement) {
|
|
173
|
+
return Object.freeze([]);
|
|
174
|
+
}
|
|
108
175
|
const nodeList = [];
|
|
109
|
-
|
|
176
|
+
walkTree([...this.childNodes], node => {
|
|
110
177
|
if (filter) {
|
|
111
178
|
const filtered = filter(node);
|
|
112
179
|
if (filtered) {
|