123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348 |
- 'use strict';
- const doctype = require('parse5/lib/common/doctype');
- const { DOCUMENT_MODE } = require('parse5/lib/common/html');
- //Conversion tables for DOM Level1 structure emulation
- const nodeTypes = {
- element: 1,
- text: 3,
- cdata: 4,
- comment: 8
- };
- const nodePropertyShorthands = {
- tagName: 'name',
- childNodes: 'children',
- parentNode: 'parent',
- previousSibling: 'prev',
- nextSibling: 'next',
- nodeValue: 'data'
- };
- //Node
- class Node {
- constructor(props) {
- for (const key of Object.keys(props)) {
- this[key] = props[key];
- }
- }
- get firstChild() {
- const children = this.children;
- return (children && children[0]) || null;
- }
- get lastChild() {
- const children = this.children;
- return (children && children[children.length - 1]) || null;
- }
- get nodeType() {
- return nodeTypes[this.type] || nodeTypes.element;
- }
- }
- Object.keys(nodePropertyShorthands).forEach(key => {
- const shorthand = nodePropertyShorthands[key];
- Object.defineProperty(Node.prototype, key, {
- get: function() {
- return this[shorthand] || null;
- },
- set: function(val) {
- this[shorthand] = val;
- return val;
- }
- });
- });
- //Node construction
- exports.createDocument = function() {
- return new Node({
- type: 'root',
- name: 'root',
- parent: null,
- prev: null,
- next: null,
- children: [],
- 'x-mode': DOCUMENT_MODE.NO_QUIRKS
- });
- };
- exports.createDocumentFragment = function() {
- return new Node({
- type: 'root',
- name: 'root',
- parent: null,
- prev: null,
- next: null,
- children: []
- });
- };
- exports.createElement = function(tagName, namespaceURI, attrs) {
- const attribs = Object.create(null);
- const attribsNamespace = Object.create(null);
- const attribsPrefix = Object.create(null);
- for (let i = 0; i < attrs.length; i++) {
- const attrName = attrs[i].name;
- attribs[attrName] = attrs[i].value;
- attribsNamespace[attrName] = attrs[i].namespace;
- attribsPrefix[attrName] = attrs[i].prefix;
- }
- return new Node({
- type: tagName === 'script' || tagName === 'style' ? tagName : 'tag',
- name: tagName,
- namespace: namespaceURI,
- attribs: attribs,
- 'x-attribsNamespace': attribsNamespace,
- 'x-attribsPrefix': attribsPrefix,
- children: [],
- parent: null,
- prev: null,
- next: null
- });
- };
- exports.createCommentNode = function(data) {
- return new Node({
- type: 'comment',
- data: data,
- parent: null,
- prev: null,
- next: null
- });
- };
- const createTextNode = function(value) {
- return new Node({
- type: 'text',
- data: value,
- parent: null,
- prev: null,
- next: null
- });
- };
- //Tree mutation
- const appendChild = (exports.appendChild = function(parentNode, newNode) {
- const prev = parentNode.children[parentNode.children.length - 1];
- if (prev) {
- prev.next = newNode;
- newNode.prev = prev;
- }
- parentNode.children.push(newNode);
- newNode.parent = parentNode;
- });
- const insertBefore = (exports.insertBefore = function(parentNode, newNode, referenceNode) {
- const insertionIdx = parentNode.children.indexOf(referenceNode);
- const prev = referenceNode.prev;
- if (prev) {
- prev.next = newNode;
- newNode.prev = prev;
- }
- referenceNode.prev = newNode;
- newNode.next = referenceNode;
- parentNode.children.splice(insertionIdx, 0, newNode);
- newNode.parent = parentNode;
- });
- exports.setTemplateContent = function(templateElement, contentElement) {
- appendChild(templateElement, contentElement);
- };
- exports.getTemplateContent = function(templateElement) {
- return templateElement.children[0];
- };
- exports.setDocumentType = function(document, name, publicId, systemId) {
- const data = doctype.serializeContent(name, publicId, systemId);
- let doctypeNode = null;
- for (let i = 0; i < document.children.length; i++) {
- if (document.children[i].type === 'directive' && document.children[i].name === '!doctype') {
- doctypeNode = document.children[i];
- break;
- }
- }
- if (doctypeNode) {
- doctypeNode.data = data;
- doctypeNode['x-name'] = name;
- doctypeNode['x-publicId'] = publicId;
- doctypeNode['x-systemId'] = systemId;
- } else {
- appendChild(
- document,
- new Node({
- type: 'directive',
- name: '!doctype',
- data: data,
- 'x-name': name,
- 'x-publicId': publicId,
- 'x-systemId': systemId
- })
- );
- }
- };
- exports.setDocumentMode = function(document, mode) {
- document['x-mode'] = mode;
- };
- exports.getDocumentMode = function(document) {
- return document['x-mode'];
- };
- exports.detachNode = function(node) {
- if (node.parent) {
- const idx = node.parent.children.indexOf(node);
- const prev = node.prev;
- const next = node.next;
- node.prev = null;
- node.next = null;
- if (prev) {
- prev.next = next;
- }
- if (next) {
- next.prev = prev;
- }
- node.parent.children.splice(idx, 1);
- node.parent = null;
- }
- };
- exports.insertText = function(parentNode, text) {
- const lastChild = parentNode.children[parentNode.children.length - 1];
- if (lastChild && lastChild.type === 'text') {
- lastChild.data += text;
- } else {
- appendChild(parentNode, createTextNode(text));
- }
- };
- exports.insertTextBefore = function(parentNode, text, referenceNode) {
- const prevNode = parentNode.children[parentNode.children.indexOf(referenceNode) - 1];
- if (prevNode && prevNode.type === 'text') {
- prevNode.data += text;
- } else {
- insertBefore(parentNode, createTextNode(text), referenceNode);
- }
- };
- exports.adoptAttributes = function(recipient, attrs) {
- for (let i = 0; i < attrs.length; i++) {
- const attrName = attrs[i].name;
- if (typeof recipient.attribs[attrName] === 'undefined') {
- recipient.attribs[attrName] = attrs[i].value;
- recipient['x-attribsNamespace'][attrName] = attrs[i].namespace;
- recipient['x-attribsPrefix'][attrName] = attrs[i].prefix;
- }
- }
- };
- //Tree traversing
- exports.getFirstChild = function(node) {
- return node.children[0];
- };
- exports.getChildNodes = function(node) {
- return node.children;
- };
- exports.getParentNode = function(node) {
- return node.parent;
- };
- exports.getAttrList = function(element) {
- const attrList = [];
- for (const name in element.attribs) {
- attrList.push({
- name: name,
- value: element.attribs[name],
- namespace: element['x-attribsNamespace'][name],
- prefix: element['x-attribsPrefix'][name]
- });
- }
- return attrList;
- };
- //Node data
- exports.getTagName = function(element) {
- return element.name;
- };
- exports.getNamespaceURI = function(element) {
- return element.namespace;
- };
- exports.getTextNodeContent = function(textNode) {
- return textNode.data;
- };
- exports.getCommentNodeContent = function(commentNode) {
- return commentNode.data;
- };
- exports.getDocumentTypeNodeName = function(doctypeNode) {
- return doctypeNode['x-name'];
- };
- exports.getDocumentTypeNodePublicId = function(doctypeNode) {
- return doctypeNode['x-publicId'];
- };
- exports.getDocumentTypeNodeSystemId = function(doctypeNode) {
- return doctypeNode['x-systemId'];
- };
- //Node types
- exports.isTextNode = function(node) {
- return node.type === 'text';
- };
- exports.isCommentNode = function(node) {
- return node.type === 'comment';
- };
- exports.isDocumentTypeNode = function(node) {
- return node.type === 'directive' && node.name === '!doctype';
- };
- exports.isElementNode = function(node) {
- return !!node.attribs;
- };
- // Source code location
- exports.setNodeSourceCodeLocation = function(node, location) {
- node.sourceCodeLocation = location;
- };
- exports.getNodeSourceCodeLocation = function(node) {
- return node.sourceCodeLocation;
- };
- exports.updateNodeSourceCodeLocation = function(node, endLocation) {
- node.sourceCodeLocation = Object.assign(node.sourceCodeLocation, endLocation);
- };
|