-
Notifications
You must be signed in to change notification settings - Fork 83
/
parse-json.js
131 lines (121 loc) · 3.97 KB
/
parse-json.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import {
NODE_END,
ELEMENT_NODE,
ATTRIBUTE_NODE,
TEXT_NODE,
CDATA_SECTION_NODE,
COMMENT_NODE,
DOCUMENT_NODE,
DOCUMENT_TYPE_NODE,
DOCUMENT_FRAGMENT_NODE
} from './constants.js';
import {END, PREV} from './symbols.js';
import {htmlClasses} from './register-html-class.js';
import {knownBoundaries, knownSiblings} from './utils.js';
import {Attr} from '../interface/attr.js';
import {CDATASection} from '../interface/cdata-section.js';
import {Comment} from '../interface/comment.js';
import {DocumentType} from '../interface/document-type.js';
import {Text} from '../interface/text.js';
import {HTMLDocument} from '../html/document.js';
import {HTMLElement} from '../html/element.js';
import {SVGElement} from '../svg/element.js';
const {parse} = JSON;
const append = (parentNode, node, end) => {
node.parentNode = parentNode;
knownSiblings(end[PREV], node, end);
};
const createHTMLElement = (ownerDocument, localName) => {
if (htmlClasses.has(localName)) {
const Class = htmlClasses.get(localName);
return new Class(ownerDocument, localName);
}
return new HTMLElement(ownerDocument, localName);
};
/**
* @typedef {number|string} jsdonValue - either a node type or its content
*/
/**
* Given a stringified, or arrayfied DOM element, returns an HTMLDocument
* that represent the content of such string, or array.
* @param {string|jsdonValue[]} value
* @returns {HTMLDocument}
*/
export const parseJSON = value => {
const array = typeof value === 'string' ? parse(value) : value;
const {length} = array;
const document = new HTMLDocument;
let parentNode = document, end = parentNode[END], svg = false, i = 0;
while (i < length) {
let nodeType = array[i++];
switch (nodeType) {
case ELEMENT_NODE: {
const localName = array[i++];
const isSVG = svg || localName === 'svg' || localName === 'SVG';
const element = isSVG ?
new SVGElement(document, localName, parentNode.ownerSVGElement || null) :
createHTMLElement(document, localName);
knownBoundaries(end[PREV], element, end);
element.parentNode = parentNode;
parentNode = element;
end = parentNode[END];
svg = isSVG;
break;
}
case ATTRIBUTE_NODE: {
const name = array[i++];
const value = typeof array[i] === 'string' ? array[i++] : '';
const attr = new Attr(document, name, value);
attr.ownerElement = parentNode;
knownSiblings(end[PREV], attr, end);
break;
}
case TEXT_NODE:
append(parentNode, new Text(document, array[i++]), end);
break;
case COMMENT_NODE:
append(parentNode, new Comment(document, array[i++]), end);
break;
case CDATA_SECTION_NODE:
append(parentNode, new CDATASection(document, array[i++]), end);
break;
case DOCUMENT_TYPE_NODE: {
const args = [document];
while (typeof array[i] === 'string')
args.push(array[i++]);
if (args.length === 3 && /\.dtd$/i.test(args[2]))
args.splice(2, 0, '');
append(parentNode, new DocumentType(...args), end);
break;
}
case DOCUMENT_FRAGMENT_NODE:
parentNode = document.createDocumentFragment();
end = parentNode[END];
/* eslint no-fallthrough:0 */
case DOCUMENT_NODE:
break;
default:
do {
nodeType -= NODE_END;
if (svg && !parentNode.ownerSVGElement)
svg = false;
parentNode = parentNode.parentNode || parentNode;
} while (nodeType < 0);
end = parentNode[END];
break;
}
}
switch (i && array[0]) {
case ELEMENT_NODE:
return document.firstElementChild;
case DOCUMENT_FRAGMENT_NODE:
return parentNode;
}
return document;
};
/**
*
* @param {Document|Element} node the Document or Element to serialize
* @returns {jsdonValue[]} the linear jsdon serialized array
*/
export const toJSON = node => node.toJSON();