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

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();