@cjser/parse5 0.0.0-stage → 8.1.0-cjser.2
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 +19 -0
- package/README.md +42 -2
- package/dist/common/doctype.d.ts +4 -0
- package/dist/common/doctype.js +114 -0
- package/dist/common/error-codes.d.ts +67 -0
- package/dist/common/error-codes.js +63 -0
- package/dist/common/foreign-content.d.ts +9 -0
- package/dist/common/foreign-content.js +228 -0
- package/dist/common/html.d.ts +289 -0
- package/dist/common/html.js +523 -0
- package/dist/common/token.d.ts +84 -0
- package/dist/common/token.js +20 -0
- package/dist/common/unicode.d.ts +42 -0
- package/dist/common/unicode.js +62 -0
- package/dist/index.d.ts +71 -0
- package/dist/index.js +42 -0
- package/dist/parser/formatting-element-list.d.ts +36 -0
- package/dist/parser/formatting-element-list.js +110 -0
- package/dist/parser/index.d.ts +226 -0
- package/dist/parser/index.js +3274 -0
- package/dist/parser/open-element-stack.d.ts +53 -0
- package/dist/parser/open-element-stack.js +324 -0
- package/dist/serializer/index.d.ts +60 -0
- package/dist/serializer/index.js +168 -0
- package/dist/tokenizer/index.d.ts +274 -0
- package/dist/tokenizer/index.js +2908 -0
- package/dist/tokenizer/preprocessor.d.ts +39 -0
- package/dist/tokenizer/preprocessor.js +205 -0
- package/dist/tree-adapters/default.d.ts +84 -0
- package/dist/tree-adapters/default.js +173 -0
- package/dist/tree-adapters/interface.d.ts +272 -0
- package/dist/tree-adapters/interface.js +1 -0
- package/dist-cjser/index.cjs +7841 -0
- package/package.json +82 -4
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { ERR, type ParserError, type ParserErrorHandler } from '../common/error-codes.js';
|
|
2
|
+
export declare class Preprocessor {
|
|
3
|
+
private handler;
|
|
4
|
+
html: string;
|
|
5
|
+
pos: number;
|
|
6
|
+
private lastGapPos;
|
|
7
|
+
private gapStack;
|
|
8
|
+
private skipNextNewLine;
|
|
9
|
+
lastChunkWritten: boolean;
|
|
10
|
+
endOfChunkHit: boolean;
|
|
11
|
+
bufferWaterline: number;
|
|
12
|
+
private isEol;
|
|
13
|
+
private lineStartPos;
|
|
14
|
+
droppedBufferSize: number;
|
|
15
|
+
line: number;
|
|
16
|
+
constructor(handler: {
|
|
17
|
+
onParseError?: ParserErrorHandler | null;
|
|
18
|
+
});
|
|
19
|
+
/** The column on the current line. If we just saw a gap (eg. a surrogate pair), return the index before. */
|
|
20
|
+
get col(): number;
|
|
21
|
+
/** The index in `html` at which the current code point starts (the high surrogate of a pair). */
|
|
22
|
+
get codePointStart(): number;
|
|
23
|
+
/** The offset of the current code point, before the second unit of a surrogate pair. */
|
|
24
|
+
get offset(): number;
|
|
25
|
+
getError(code: ERR, cpOffset: number): ParserError;
|
|
26
|
+
private lastErrOffset;
|
|
27
|
+
private _err;
|
|
28
|
+
private _addGap;
|
|
29
|
+
private _processSurrogate;
|
|
30
|
+
willDropParsedChunk(): boolean;
|
|
31
|
+
dropParsedChunk(): void;
|
|
32
|
+
write(chunk: string, isLastChunk: boolean): void;
|
|
33
|
+
insertHtmlAtCurrentPos(chunk: string): void;
|
|
34
|
+
startsWith(pattern: string, caseSensitive: boolean): boolean;
|
|
35
|
+
peek(offset: number): number;
|
|
36
|
+
advance(): number;
|
|
37
|
+
private _checkForProblematicCharacters;
|
|
38
|
+
retreat(count: number): void;
|
|
39
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { CODE_POINTS as $, getSurrogatePairCodePoint, isControlCodePoint, isSurrogate, isSurrogatePair, isUndefinedCodePoint, } from '../common/unicode.js';
|
|
2
|
+
import { ERR } from '../common/error-codes.js';
|
|
3
|
+
//Const
|
|
4
|
+
const DEFAULT_BUFFER_WATERLINE = 1 << 16;
|
|
5
|
+
//Preprocessor
|
|
6
|
+
//NOTE: HTML input preprocessing
|
|
7
|
+
//(see: http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#preprocessing-the-input-stream)
|
|
8
|
+
export class Preprocessor {
|
|
9
|
+
constructor(handler) {
|
|
10
|
+
this.handler = handler;
|
|
11
|
+
this.html = '';
|
|
12
|
+
this.pos = -1;
|
|
13
|
+
// NOTE: Initial `lastGapPos` is -2, to ensure `col` on initialisation is 0
|
|
14
|
+
this.lastGapPos = -2;
|
|
15
|
+
this.gapStack = [];
|
|
16
|
+
this.skipNextNewLine = false;
|
|
17
|
+
this.lastChunkWritten = false;
|
|
18
|
+
this.endOfChunkHit = false;
|
|
19
|
+
this.bufferWaterline = DEFAULT_BUFFER_WATERLINE;
|
|
20
|
+
this.isEol = false;
|
|
21
|
+
this.lineStartPos = 0;
|
|
22
|
+
this.droppedBufferSize = 0;
|
|
23
|
+
this.line = 1;
|
|
24
|
+
//NOTE: avoid reporting errors twice on advance/retreat
|
|
25
|
+
this.lastErrOffset = -1;
|
|
26
|
+
}
|
|
27
|
+
/** The column on the current line. If we just saw a gap (eg. a surrogate pair), return the index before. */
|
|
28
|
+
get col() {
|
|
29
|
+
return this.pos - this.lineStartPos + Number(this.lastGapPos !== this.pos);
|
|
30
|
+
}
|
|
31
|
+
/** The index in `html` at which the current code point starts (the high surrogate of a pair). */
|
|
32
|
+
get codePointStart() {
|
|
33
|
+
return this.pos - Number(this.lastGapPos === this.pos);
|
|
34
|
+
}
|
|
35
|
+
/** The offset of the current code point, before the second unit of a surrogate pair. */
|
|
36
|
+
get offset() {
|
|
37
|
+
return this.droppedBufferSize + this.codePointStart;
|
|
38
|
+
}
|
|
39
|
+
getError(code, cpOffset) {
|
|
40
|
+
const { line, col, offset } = this;
|
|
41
|
+
const startCol = col + cpOffset;
|
|
42
|
+
const startOffset = offset + cpOffset;
|
|
43
|
+
return {
|
|
44
|
+
code,
|
|
45
|
+
startLine: line,
|
|
46
|
+
endLine: line,
|
|
47
|
+
startCol,
|
|
48
|
+
endCol: startCol,
|
|
49
|
+
startOffset,
|
|
50
|
+
endOffset: startOffset,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
_err(code) {
|
|
54
|
+
if (this.handler.onParseError && this.lastErrOffset !== this.offset) {
|
|
55
|
+
this.lastErrOffset = this.offset;
|
|
56
|
+
this.handler.onParseError(this.getError(code, 0));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
_addGap() {
|
|
60
|
+
this.gapStack.push(this.lastGapPos);
|
|
61
|
+
this.lastGapPos = this.pos;
|
|
62
|
+
}
|
|
63
|
+
_processSurrogate(cp) {
|
|
64
|
+
//NOTE: try to peek a surrogate pair
|
|
65
|
+
if (this.pos !== this.html.length - 1) {
|
|
66
|
+
const nextCp = this.html.charCodeAt(this.pos + 1);
|
|
67
|
+
if (isSurrogatePair(nextCp)) {
|
|
68
|
+
//NOTE: we have a surrogate pair. Peek pair character and recalculate code point.
|
|
69
|
+
this.pos++;
|
|
70
|
+
//NOTE: add a gap that should be avoided during retreat
|
|
71
|
+
this._addGap();
|
|
72
|
+
return getSurrogatePairCodePoint(cp, nextCp);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
//NOTE: we are at the end of a chunk, therefore we can't infer the surrogate pair yet.
|
|
76
|
+
else if (!this.lastChunkWritten) {
|
|
77
|
+
this.endOfChunkHit = true;
|
|
78
|
+
return $.EOF;
|
|
79
|
+
}
|
|
80
|
+
//NOTE: isolated surrogate
|
|
81
|
+
this._err(ERR.surrogateInInputStream);
|
|
82
|
+
return cp;
|
|
83
|
+
}
|
|
84
|
+
willDropParsedChunk() {
|
|
85
|
+
return this.pos > this.bufferWaterline;
|
|
86
|
+
}
|
|
87
|
+
dropParsedChunk() {
|
|
88
|
+
if (this.willDropParsedChunk()) {
|
|
89
|
+
// Retain the entire current code point, including both units of a surrogate pair.
|
|
90
|
+
const droppedSize = this.offset - this.droppedBufferSize;
|
|
91
|
+
this.html = this.html.substring(droppedSize);
|
|
92
|
+
this.lineStartPos -= droppedSize;
|
|
93
|
+
this.droppedBufferSize += droppedSize;
|
|
94
|
+
this.pos -= droppedSize;
|
|
95
|
+
this.lastGapPos = -2;
|
|
96
|
+
this.gapStack.length = 0;
|
|
97
|
+
if (this.pos > 0)
|
|
98
|
+
this._addGap();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
write(chunk, isLastChunk) {
|
|
102
|
+
if (this.html.length > 0) {
|
|
103
|
+
this.html += chunk;
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
this.html = chunk;
|
|
107
|
+
}
|
|
108
|
+
this.endOfChunkHit = false;
|
|
109
|
+
this.lastChunkWritten = isLastChunk;
|
|
110
|
+
}
|
|
111
|
+
insertHtmlAtCurrentPos(chunk) {
|
|
112
|
+
this.html = this.html.substring(0, this.pos + 1) + chunk + this.html.substring(this.pos + 1);
|
|
113
|
+
this.endOfChunkHit = false;
|
|
114
|
+
}
|
|
115
|
+
startsWith(pattern, caseSensitive) {
|
|
116
|
+
// Check if our buffer has enough characters
|
|
117
|
+
if (this.pos + pattern.length > this.html.length) {
|
|
118
|
+
this.endOfChunkHit = !this.lastChunkWritten;
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
if (caseSensitive) {
|
|
122
|
+
return this.html.startsWith(pattern, this.pos);
|
|
123
|
+
}
|
|
124
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
125
|
+
const cp = this.html.charCodeAt(this.pos + i) | 0x20;
|
|
126
|
+
if (cp !== pattern.charCodeAt(i)) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
peek(offset) {
|
|
133
|
+
const pos = this.pos + offset;
|
|
134
|
+
if (pos >= this.html.length) {
|
|
135
|
+
this.endOfChunkHit = !this.lastChunkWritten;
|
|
136
|
+
return $.EOF;
|
|
137
|
+
}
|
|
138
|
+
const code = this.html.charCodeAt(pos);
|
|
139
|
+
return code === $.CARRIAGE_RETURN ? $.LINE_FEED : code;
|
|
140
|
+
}
|
|
141
|
+
advance() {
|
|
142
|
+
this.pos++;
|
|
143
|
+
//NOTE: LF should be in the last column of the line
|
|
144
|
+
if (this.isEol) {
|
|
145
|
+
this.isEol = false;
|
|
146
|
+
this.line++;
|
|
147
|
+
this.lineStartPos = this.pos;
|
|
148
|
+
}
|
|
149
|
+
if (this.pos >= this.html.length) {
|
|
150
|
+
this.endOfChunkHit = !this.lastChunkWritten;
|
|
151
|
+
return $.EOF;
|
|
152
|
+
}
|
|
153
|
+
let cp = this.html.charCodeAt(this.pos);
|
|
154
|
+
//NOTE: all U+000D CARRIAGE RETURN (CR) characters must be converted to U+000A LINE FEED (LF) characters
|
|
155
|
+
if (cp === $.CARRIAGE_RETURN) {
|
|
156
|
+
this.isEol = true;
|
|
157
|
+
this.skipNextNewLine = true;
|
|
158
|
+
return $.LINE_FEED;
|
|
159
|
+
}
|
|
160
|
+
//NOTE: any U+000A LINE FEED (LF) characters that immediately follow a U+000D CARRIAGE RETURN (CR) character
|
|
161
|
+
//must be ignored.
|
|
162
|
+
if (cp === $.LINE_FEED) {
|
|
163
|
+
this.isEol = true;
|
|
164
|
+
if (this.skipNextNewLine) {
|
|
165
|
+
// `line` will be bumped again in the recursive call.
|
|
166
|
+
this.line--;
|
|
167
|
+
this.skipNextNewLine = false;
|
|
168
|
+
this._addGap();
|
|
169
|
+
return this.advance();
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
this.skipNextNewLine = false;
|
|
173
|
+
if (isSurrogate(cp)) {
|
|
174
|
+
cp = this._processSurrogate(cp);
|
|
175
|
+
}
|
|
176
|
+
//OPTIMIZATION: first check if code point is in the common allowed
|
|
177
|
+
//range (ASCII alphanumeric, whitespaces, big chunk of BMP)
|
|
178
|
+
//before going into detailed performance cost validation.
|
|
179
|
+
const isCommonValidRange = this.handler.onParseError === null ||
|
|
180
|
+
(cp > 0x1f && cp < 0x7f) ||
|
|
181
|
+
cp === $.LINE_FEED ||
|
|
182
|
+
cp === $.CARRIAGE_RETURN ||
|
|
183
|
+
(cp > 0x9f && cp < 64976);
|
|
184
|
+
if (!isCommonValidRange) {
|
|
185
|
+
this._checkForProblematicCharacters(cp);
|
|
186
|
+
}
|
|
187
|
+
return cp;
|
|
188
|
+
}
|
|
189
|
+
_checkForProblematicCharacters(cp) {
|
|
190
|
+
if (isControlCodePoint(cp)) {
|
|
191
|
+
this._err(ERR.controlCharacterInInputStream);
|
|
192
|
+
}
|
|
193
|
+
else if (isUndefinedCodePoint(cp)) {
|
|
194
|
+
this._err(ERR.noncharacterInInputStream);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
retreat(count) {
|
|
198
|
+
this.pos -= count;
|
|
199
|
+
while (this.pos < this.lastGapPos) {
|
|
200
|
+
this.lastGapPos = this.gapStack.pop();
|
|
201
|
+
this.pos--;
|
|
202
|
+
}
|
|
203
|
+
this.isEol = false;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { DOCUMENT_MODE, type NS } from '../common/html.js';
|
|
2
|
+
import type { Attribute, Location, ElementLocation } from '../common/token.js';
|
|
3
|
+
import type { TreeAdapter, TreeAdapterTypeMap } from './interface.js';
|
|
4
|
+
export interface Document {
|
|
5
|
+
/** The name of the node. */
|
|
6
|
+
nodeName: '#document';
|
|
7
|
+
/**
|
|
8
|
+
* Document mode.
|
|
9
|
+
*
|
|
10
|
+
* @see {@link DOCUMENT_MODE} */
|
|
11
|
+
mode: DOCUMENT_MODE;
|
|
12
|
+
/** The node's children. */
|
|
13
|
+
childNodes: ChildNode[];
|
|
14
|
+
/** Comment source code location info. Available if location info is enabled. */
|
|
15
|
+
sourceCodeLocation?: Location | null;
|
|
16
|
+
}
|
|
17
|
+
export interface DocumentFragment {
|
|
18
|
+
/** The name of the node. */
|
|
19
|
+
nodeName: '#document-fragment';
|
|
20
|
+
/** The node's children. */
|
|
21
|
+
childNodes: ChildNode[];
|
|
22
|
+
/** Comment source code location info. Available if location info is enabled. */
|
|
23
|
+
sourceCodeLocation?: Location | null;
|
|
24
|
+
}
|
|
25
|
+
export interface Element {
|
|
26
|
+
/** Element tag name. Same as {@link tagName}. */
|
|
27
|
+
nodeName: string;
|
|
28
|
+
/** Element tag name. Same as {@link nodeName}. */
|
|
29
|
+
tagName: string;
|
|
30
|
+
/** List of element attributes. */
|
|
31
|
+
attrs: Attribute[];
|
|
32
|
+
/** Element namespace. */
|
|
33
|
+
namespaceURI: NS;
|
|
34
|
+
/** Element source code location info, with attributes. Available if location info is enabled. */
|
|
35
|
+
sourceCodeLocation?: ElementLocation | null;
|
|
36
|
+
/** Parent node. */
|
|
37
|
+
parentNode: ParentNode | null;
|
|
38
|
+
/** The node's children. */
|
|
39
|
+
childNodes: ChildNode[];
|
|
40
|
+
}
|
|
41
|
+
export interface CommentNode {
|
|
42
|
+
/** The name of the node. */
|
|
43
|
+
nodeName: '#comment';
|
|
44
|
+
/** Parent node. */
|
|
45
|
+
parentNode: ParentNode | null;
|
|
46
|
+
/** Comment text. */
|
|
47
|
+
data: string;
|
|
48
|
+
/** Comment source code location info. Available if location info is enabled. */
|
|
49
|
+
sourceCodeLocation?: Location | null;
|
|
50
|
+
}
|
|
51
|
+
export interface TextNode {
|
|
52
|
+
nodeName: '#text';
|
|
53
|
+
/** Parent node. */
|
|
54
|
+
parentNode: ParentNode | null;
|
|
55
|
+
/** Text content. */
|
|
56
|
+
value: string;
|
|
57
|
+
/** Comment source code location info. Available if location info is enabled. */
|
|
58
|
+
sourceCodeLocation?: Location | null;
|
|
59
|
+
}
|
|
60
|
+
export interface Template extends Element {
|
|
61
|
+
nodeName: 'template';
|
|
62
|
+
tagName: 'template';
|
|
63
|
+
/** The content of a `template` tag. */
|
|
64
|
+
content: DocumentFragment;
|
|
65
|
+
}
|
|
66
|
+
export interface DocumentType {
|
|
67
|
+
/** The name of the node. */
|
|
68
|
+
nodeName: '#documentType';
|
|
69
|
+
/** Parent node. */
|
|
70
|
+
parentNode: ParentNode | null;
|
|
71
|
+
/** Document type name. */
|
|
72
|
+
name: string;
|
|
73
|
+
/** Document type public identifier. */
|
|
74
|
+
publicId: string;
|
|
75
|
+
/** Document type system identifier. */
|
|
76
|
+
systemId: string;
|
|
77
|
+
/** Comment source code location info. Available if location info is enabled. */
|
|
78
|
+
sourceCodeLocation?: Location | null;
|
|
79
|
+
}
|
|
80
|
+
export type ParentNode = Document | DocumentFragment | Element | Template;
|
|
81
|
+
export type ChildNode = Element | Template | CommentNode | TextNode | DocumentType;
|
|
82
|
+
export type Node = ParentNode | ChildNode;
|
|
83
|
+
export type DefaultTreeAdapterMap = TreeAdapterTypeMap<Node, ParentNode, ChildNode, Document, DocumentFragment, Element, CommentNode, TextNode, Template, DocumentType>;
|
|
84
|
+
export declare const defaultTreeAdapter: TreeAdapter<DefaultTreeAdapterMap>;
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { DOCUMENT_MODE } from '../common/html.js';
|
|
2
|
+
export const defaultTreeAdapter = {
|
|
3
|
+
//Node construction
|
|
4
|
+
createDocument() {
|
|
5
|
+
return {
|
|
6
|
+
nodeName: '#document',
|
|
7
|
+
mode: DOCUMENT_MODE.NO_QUIRKS,
|
|
8
|
+
childNodes: [],
|
|
9
|
+
};
|
|
10
|
+
},
|
|
11
|
+
createDocumentFragment() {
|
|
12
|
+
return {
|
|
13
|
+
nodeName: '#document-fragment',
|
|
14
|
+
childNodes: [],
|
|
15
|
+
};
|
|
16
|
+
},
|
|
17
|
+
createElement(tagName, namespaceURI, attrs) {
|
|
18
|
+
return {
|
|
19
|
+
nodeName: tagName,
|
|
20
|
+
tagName,
|
|
21
|
+
attrs,
|
|
22
|
+
namespaceURI,
|
|
23
|
+
childNodes: [],
|
|
24
|
+
parentNode: null,
|
|
25
|
+
};
|
|
26
|
+
},
|
|
27
|
+
createCommentNode(data) {
|
|
28
|
+
return {
|
|
29
|
+
nodeName: '#comment',
|
|
30
|
+
data,
|
|
31
|
+
parentNode: null,
|
|
32
|
+
};
|
|
33
|
+
},
|
|
34
|
+
createTextNode(value) {
|
|
35
|
+
return {
|
|
36
|
+
nodeName: '#text',
|
|
37
|
+
value,
|
|
38
|
+
parentNode: null,
|
|
39
|
+
};
|
|
40
|
+
},
|
|
41
|
+
//Tree mutation
|
|
42
|
+
appendChild(parentNode, newNode) {
|
|
43
|
+
parentNode.childNodes.push(newNode);
|
|
44
|
+
newNode.parentNode = parentNode;
|
|
45
|
+
},
|
|
46
|
+
insertBefore(parentNode, newNode, referenceNode) {
|
|
47
|
+
const insertionIdx = parentNode.childNodes.indexOf(referenceNode);
|
|
48
|
+
parentNode.childNodes.splice(insertionIdx, 0, newNode);
|
|
49
|
+
newNode.parentNode = parentNode;
|
|
50
|
+
},
|
|
51
|
+
setTemplateContent(templateElement, contentElement) {
|
|
52
|
+
templateElement.content = contentElement;
|
|
53
|
+
},
|
|
54
|
+
getTemplateContent(templateElement) {
|
|
55
|
+
return templateElement.content;
|
|
56
|
+
},
|
|
57
|
+
setDocumentType(document, name, publicId, systemId) {
|
|
58
|
+
const doctypeNode = document.childNodes.find((node) => node.nodeName === '#documentType');
|
|
59
|
+
if (doctypeNode) {
|
|
60
|
+
doctypeNode.name = name;
|
|
61
|
+
doctypeNode.publicId = publicId;
|
|
62
|
+
doctypeNode.systemId = systemId;
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
const node = {
|
|
66
|
+
nodeName: '#documentType',
|
|
67
|
+
name,
|
|
68
|
+
publicId,
|
|
69
|
+
systemId,
|
|
70
|
+
parentNode: null,
|
|
71
|
+
};
|
|
72
|
+
defaultTreeAdapter.appendChild(document, node);
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
setDocumentMode(document, mode) {
|
|
76
|
+
document.mode = mode;
|
|
77
|
+
},
|
|
78
|
+
getDocumentMode(document) {
|
|
79
|
+
return document.mode;
|
|
80
|
+
},
|
|
81
|
+
detachNode(node) {
|
|
82
|
+
if (node.parentNode) {
|
|
83
|
+
const idx = node.parentNode.childNodes.indexOf(node);
|
|
84
|
+
node.parentNode.childNodes.splice(idx, 1);
|
|
85
|
+
node.parentNode = null;
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
insertText(parentNode, text) {
|
|
89
|
+
if (parentNode.childNodes.length > 0) {
|
|
90
|
+
const prevNode = parentNode.childNodes[parentNode.childNodes.length - 1];
|
|
91
|
+
if (defaultTreeAdapter.isTextNode(prevNode)) {
|
|
92
|
+
prevNode.value += text;
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
defaultTreeAdapter.appendChild(parentNode, defaultTreeAdapter.createTextNode(text));
|
|
97
|
+
},
|
|
98
|
+
insertTextBefore(parentNode, text, referenceNode) {
|
|
99
|
+
const prevNode = parentNode.childNodes[parentNode.childNodes.indexOf(referenceNode) - 1];
|
|
100
|
+
if (prevNode && defaultTreeAdapter.isTextNode(prevNode)) {
|
|
101
|
+
prevNode.value += text;
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
defaultTreeAdapter.insertBefore(parentNode, defaultTreeAdapter.createTextNode(text), referenceNode);
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
adoptAttributes(recipient, attrs) {
|
|
108
|
+
const recipientAttrsMap = new Set(recipient.attrs.map((attr) => attr.name));
|
|
109
|
+
for (let j = 0; j < attrs.length; j++) {
|
|
110
|
+
if (!recipientAttrsMap.has(attrs[j].name)) {
|
|
111
|
+
recipient.attrs.push(attrs[j]);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
//Tree traversing
|
|
116
|
+
getFirstChild(node) {
|
|
117
|
+
return node.childNodes[0];
|
|
118
|
+
},
|
|
119
|
+
getChildNodes(node) {
|
|
120
|
+
return node.childNodes;
|
|
121
|
+
},
|
|
122
|
+
getParentNode(node) {
|
|
123
|
+
return node.parentNode;
|
|
124
|
+
},
|
|
125
|
+
getAttrList(element) {
|
|
126
|
+
return element.attrs;
|
|
127
|
+
},
|
|
128
|
+
//Node data
|
|
129
|
+
getTagName(element) {
|
|
130
|
+
return element.tagName;
|
|
131
|
+
},
|
|
132
|
+
getNamespaceURI(element) {
|
|
133
|
+
return element.namespaceURI;
|
|
134
|
+
},
|
|
135
|
+
getTextNodeContent(textNode) {
|
|
136
|
+
return textNode.value;
|
|
137
|
+
},
|
|
138
|
+
getCommentNodeContent(commentNode) {
|
|
139
|
+
return commentNode.data;
|
|
140
|
+
},
|
|
141
|
+
getDocumentTypeNodeName(doctypeNode) {
|
|
142
|
+
return doctypeNode.name;
|
|
143
|
+
},
|
|
144
|
+
getDocumentTypeNodePublicId(doctypeNode) {
|
|
145
|
+
return doctypeNode.publicId;
|
|
146
|
+
},
|
|
147
|
+
getDocumentTypeNodeSystemId(doctypeNode) {
|
|
148
|
+
return doctypeNode.systemId;
|
|
149
|
+
},
|
|
150
|
+
//Node types
|
|
151
|
+
isTextNode(node) {
|
|
152
|
+
return node.nodeName === '#text';
|
|
153
|
+
},
|
|
154
|
+
isCommentNode(node) {
|
|
155
|
+
return node.nodeName === '#comment';
|
|
156
|
+
},
|
|
157
|
+
isDocumentTypeNode(node) {
|
|
158
|
+
return node.nodeName === '#documentType';
|
|
159
|
+
},
|
|
160
|
+
isElementNode(node) {
|
|
161
|
+
return Object.prototype.hasOwnProperty.call(node, 'tagName');
|
|
162
|
+
},
|
|
163
|
+
// Source code location
|
|
164
|
+
setNodeSourceCodeLocation(node, location) {
|
|
165
|
+
node.sourceCodeLocation = location;
|
|
166
|
+
},
|
|
167
|
+
getNodeSourceCodeLocation(node) {
|
|
168
|
+
return node.sourceCodeLocation;
|
|
169
|
+
},
|
|
170
|
+
updateNodeSourceCodeLocation(node, endLocation) {
|
|
171
|
+
node.sourceCodeLocation = { ...node.sourceCodeLocation, ...endLocation };
|
|
172
|
+
},
|
|
173
|
+
};
|