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.

135 lines
4.1 KiB
JavaScript

'use strict';
const {
NODE_END,
ELEMENT_NODE,
ATTRIBUTE_NODE,
TEXT_NODE,
CDATA_SECTION_NODE,
COMMENT_NODE,
DOCUMENT_NODE,
DOCUMENT_TYPE_NODE,
DOCUMENT_FRAGMENT_NODE
} = require('./constants.js');
const {END, PREV} = require('./symbols.js');
const {htmlClasses} = require('./register-html-class.js');
const {knownBoundaries, knownSiblings} = require('./utils.js');
const {Attr} = require('../interface/attr.js');
const {CDATASection} = require('../interface/cdata-section.js');
const {Comment} = require('../interface/comment.js');
const {DocumentType} = require('../interface/document-type.js');
const {Text} = require('../interface/text.js');
const {HTMLDocument} = require('../html/document.js');
const {HTMLElement} = require('../html/element.js');
const {SVGElement} = require('../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}
*/
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;
};
exports.parseJSON = parseJSON;
/**
*
* @param {Document|Element} node the Document or Element to serialize
* @returns {jsdonValue[]} the linear jsdon serialized array
*/
const toJSON = node => node.toJSON();
exports.toJSON = toJSON;