You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
132 lines
4.0 KiB
JavaScript
132 lines
4.0 KiB
JavaScript
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();
|