pseudo-dom 0.14.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +167 -5
- package/browser/pseudo-dom.js +417 -0
- package/browser/pseudo-dom.min.js +1 -1
- package/dist/factories/serializeHTML.js +160 -160
- package/dist/interfaces/PseudoElement.d.ts +789 -765
- package/dist/services/ElementService.d.ts +527 -371
- package/dist/services/ElementService.js +1149 -732
- package/dist/services/ElementService.min.js +1 -1
- package/package.json +1 -1
|
@@ -1,160 +1,160 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
require('core-js/modules/esnext.iterator.constructor.js')
|
|
4
|
-
require('core-js/modules/esnext.iterator.filter.js')
|
|
5
|
-
require('core-js/modules/esnext.iterator.map.js')
|
|
6
|
-
require('core-js/modules/esnext.set.add-all.js')
|
|
7
|
-
require('core-js/modules/esnext.set.delete-all.js')
|
|
8
|
-
require('core-js/modules/esnext.set.difference.js')
|
|
9
|
-
require('core-js/modules/esnext.set.every.js')
|
|
10
|
-
require('core-js/modules/esnext.set.filter.js')
|
|
11
|
-
require('core-js/modules/esnext.set.find.js')
|
|
12
|
-
require('core-js/modules/esnext.set.intersection.js')
|
|
13
|
-
require('core-js/modules/esnext.set.is-disjoint-from.js')
|
|
14
|
-
require('core-js/modules/esnext.set.is-subset-of.js')
|
|
15
|
-
require('core-js/modules/esnext.set.is-superset-of.js')
|
|
16
|
-
require('core-js/modules/esnext.set.join.js')
|
|
17
|
-
require('core-js/modules/esnext.set.map.js')
|
|
18
|
-
require('core-js/modules/esnext.set.reduce.js')
|
|
19
|
-
require('core-js/modules/esnext.set.some.js')
|
|
20
|
-
require('core-js/modules/esnext.set.symmetric-difference.js')
|
|
21
|
-
require('core-js/modules/esnext.set.union.js')
|
|
22
|
-
Object.defineProperty(exports, '__esModule', {
|
|
23
|
-
value: true
|
|
24
|
-
})
|
|
25
|
-
exports.prettyPrint = exports.serializeOuter = exports.serializeChildren = void 0
|
|
26
|
-
/**
|
|
27
|
-
* @file Serializes pseudo-dom nodes back into an HTML string, for innerHTML / outerHTML.
|
|
28
|
-
* @author Joshua Heagle <joshuaheagle@gmail.com>
|
|
29
|
-
* @version 1.0.0
|
|
30
|
-
*/
|
|
31
|
-
const NodeService_1 = require('../services/NodeService')
|
|
32
|
-
// Void elements are self-closing and never serialize a closing tag or children (there is no HTML parsing of their
|
|
33
|
-
// content either - the DOM never lets them have children in the first place).
|
|
34
|
-
const VOID_ELEMENTS = new Set(['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr'])
|
|
35
|
-
// These property-backed names (see ElementService / HTMLElementService's constructors) are not real HTML content
|
|
36
|
-
// attributes - offsetWidth and friends describe layout, which does not exist here, and innerHTML is JS-only - so
|
|
37
|
-
// they must never appear in serialized markup, however getAttributeNames() may report them.
|
|
38
|
-
const NEVER_SERIALIZE = new Set(['innerHTML', 'clientHeight', 'clientLeft', 'clientTop', 'clientWidth', 'scrollHeight', 'scrollLeft', 'scrollTop', 'scrollWidth', 'offsetHeight', 'offsetLeft', 'offsetParent', 'offsetTop', 'offsetWidth'])
|
|
39
|
-
// The attribute name to serialize a property-backed name as, when it differs from the property name
|
|
40
|
-
const ATTRIBUTE_NAME = {
|
|
41
|
-
className: 'class'
|
|
42
|
-
}
|
|
43
|
-
// Boolean HTML attributes: present (with no value) when true, absent entirely when false
|
|
44
|
-
const BOOLEAN_ATTRIBUTES = new Set(['hidden'])
|
|
45
|
-
/**
|
|
46
|
-
* Escape text so it is safe inside HTML text content. Coerces to a string first - unlike a real DOM, pseudo-dom's
|
|
47
|
-
* setAttribute does not itself coerce (see the same note on escapeAttributeValue), and nodeValue is not guaranteed
|
|
48
|
-
* to be a string either.
|
|
49
|
-
* @param {*} text
|
|
50
|
-
* @returns {string}
|
|
51
|
-
*/
|
|
52
|
-
const escapeText = text => String(text).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
|
53
|
-
/**
|
|
54
|
-
* Escape a value so it is safe inside a double-quoted HTML attribute. Coerces to a string first: a real DOM's
|
|
55
|
-
* setAttribute always stores a string, however pseudo-dom's does not coerce what it is given, so a value set via
|
|
56
|
-
* setAttribute(name, 5) is stored (and read back by getAttribute) as the number 5, not the string '5'.
|
|
57
|
-
* @param {*} value
|
|
58
|
-
* @returns {string}
|
|
59
|
-
*/
|
|
60
|
-
const escapeAttributeValue = value => String(value).replace(/&/g, '&').replace(/"/g, '"')
|
|
61
|
-
/**
|
|
62
|
-
* Every attribute of the element, serialized (class instead of className, boolean attributes bare, the never-real
|
|
63
|
-
* mock properties left out, style added from the live CSSStyleDeclaration when it is not empty).
|
|
64
|
-
* @param {*} element
|
|
65
|
-
* @returns {string}
|
|
66
|
-
*/
|
|
67
|
-
const serializeAttributes = element => {
|
|
68
|
-
let result = (element.getAttributeNames ? element.getAttributeNames() : []).filter(name => !NEVER_SERIALIZE.has(name)).map(name => {
|
|
69
|
-
if (BOOLEAN_ATTRIBUTES.has(name)) {
|
|
70
|
-
return element[name] ? ` ${name}` : ''
|
|
71
|
-
}
|
|
72
|
-
const value = element.getAttribute(name)
|
|
73
|
-
return value === '' ? '' : ` ${ATTRIBUTE_NAME[name] || name}="${escapeAttributeValue(value)}"`
|
|
74
|
-
}).join('')
|
|
75
|
-
const style = element.style
|
|
76
|
-
if (style && style.cssText) {
|
|
77
|
-
result += ` style="${escapeAttributeValue(style.cssText)}"`
|
|
78
|
-
}
|
|
79
|
-
return result
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* One node, serialized (its own markup only - see serializeChildren for its descendants too).
|
|
83
|
-
* @param {*} node
|
|
84
|
-
* @returns {string}
|
|
85
|
-
*/
|
|
86
|
-
const serializeNode = node => {
|
|
87
|
-
if (node.nodeType === NodeService_1.NodeService.TEXT_NODE) {
|
|
88
|
-
return escapeText(node.nodeValue || '')
|
|
89
|
-
}
|
|
90
|
-
if (node.nodeType === NodeService_1.NodeService.COMMENT_NODE) {
|
|
91
|
-
return `<!--${node.nodeValue || ''}-->`
|
|
92
|
-
}
|
|
93
|
-
if (node.nodeType !== NodeService_1.NodeService.ELEMENT_NODE) {
|
|
94
|
-
return ''
|
|
95
|
-
}
|
|
96
|
-
const tagName = node.tagName
|
|
97
|
-
const attributes = serializeAttributes(node)
|
|
98
|
-
if (VOID_ELEMENTS.has(tagName)) {
|
|
99
|
-
return `<${tagName}${attributes}>`
|
|
100
|
-
}
|
|
101
|
-
return `<${tagName}${attributes}>${serializeChildren(node)}</${tagName}>`
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* A node's children, serialized in order (this is what innerHTML returns).
|
|
105
|
-
* @memberOf module:factories
|
|
106
|
-
* @param {*} node
|
|
107
|
-
* @returns {string}
|
|
108
|
-
*/
|
|
109
|
-
const serializeChildren = node => Array.from(node.childNodes).map(serializeNode).join('')
|
|
110
|
-
exports.serializeChildren = serializeChildren
|
|
111
|
-
/**
|
|
112
|
-
* An element itself, serialized with its children (this is what outerHTML returns).
|
|
113
|
-
* @memberOf module:factories
|
|
114
|
-
* @param {*} element
|
|
115
|
-
* @returns {string}
|
|
116
|
-
*/
|
|
117
|
-
const serializeOuter = element => serializeNode(element)
|
|
118
|
-
exports.serializeOuter = serializeOuter
|
|
119
|
-
/**
|
|
120
|
-
* One node, indented for readability (see prettyPrint) - unlike serializeNode, every non-empty node is its own
|
|
121
|
-
* line, so the structure of a whole tree is easy to read at a glance.
|
|
122
|
-
* @param {*} node
|
|
123
|
-
* @param {number} depth
|
|
124
|
-
* @param {string} indent
|
|
125
|
-
* @returns {string}
|
|
126
|
-
*/
|
|
127
|
-
const prettyPrintNode = (node, depth, indent) => {
|
|
128
|
-
const pad = indent.repeat(depth)
|
|
129
|
-
if (node.nodeType === NodeService_1.NodeService.TEXT_NODE) {
|
|
130
|
-
const text = escapeText((node.nodeValue || '').trim())
|
|
131
|
-
return text ? `${pad}${text}` : ''
|
|
132
|
-
}
|
|
133
|
-
if (node.nodeType === NodeService_1.NodeService.COMMENT_NODE) {
|
|
134
|
-
return `${pad}<!--${node.nodeValue || ''}-->`
|
|
135
|
-
}
|
|
136
|
-
if (node.nodeType !== NodeService_1.NodeService.ELEMENT_NODE) {
|
|
137
|
-
return ''
|
|
138
|
-
}
|
|
139
|
-
const tagName = node.tagName
|
|
140
|
-
const attributes = serializeAttributes(node)
|
|
141
|
-
if (VOID_ELEMENTS.has(tagName)) {
|
|
142
|
-
return `${pad}<${tagName}${attributes}>`
|
|
143
|
-
}
|
|
144
|
-
const children = Array.from(node.childNodes).map(child => prettyPrintNode(child, depth + 1, indent)).filter(line => line !== '')
|
|
145
|
-
if (children.length === 0) {
|
|
146
|
-
return `${pad}<${tagName}${attributes}></${tagName}>`
|
|
147
|
-
}
|
|
148
|
-
return `${pad}<${tagName}${attributes}>\n${children.join('\n')}\n${pad}</${tagName}>`
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* A node's markup, indented one level per level of nesting - a real DOM's outerHTML / innerHTML has no line breaks
|
|
152
|
-
* at all, which does not read well for a whole tree (a board full of cells, say). Useful for watching pseudo-dom-
|
|
153
|
-
* driven code run headlessly, printed to a terminal - see also logElement, which does the printing too.
|
|
154
|
-
* @memberOf module:factories
|
|
155
|
-
* @param {*} node
|
|
156
|
-
* @param {string} [indent=' '] The indentation used per level of nesting
|
|
157
|
-
* @returns {string}
|
|
158
|
-
*/
|
|
159
|
-
const prettyPrint = (node, indent = ' ') => prettyPrintNode(node, 0, indent)
|
|
160
|
-
exports.prettyPrint = prettyPrint
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
require('core-js/modules/esnext.iterator.constructor.js')
|
|
4
|
+
require('core-js/modules/esnext.iterator.filter.js')
|
|
5
|
+
require('core-js/modules/esnext.iterator.map.js')
|
|
6
|
+
require('core-js/modules/esnext.set.add-all.js')
|
|
7
|
+
require('core-js/modules/esnext.set.delete-all.js')
|
|
8
|
+
require('core-js/modules/esnext.set.difference.js')
|
|
9
|
+
require('core-js/modules/esnext.set.every.js')
|
|
10
|
+
require('core-js/modules/esnext.set.filter.js')
|
|
11
|
+
require('core-js/modules/esnext.set.find.js')
|
|
12
|
+
require('core-js/modules/esnext.set.intersection.js')
|
|
13
|
+
require('core-js/modules/esnext.set.is-disjoint-from.js')
|
|
14
|
+
require('core-js/modules/esnext.set.is-subset-of.js')
|
|
15
|
+
require('core-js/modules/esnext.set.is-superset-of.js')
|
|
16
|
+
require('core-js/modules/esnext.set.join.js')
|
|
17
|
+
require('core-js/modules/esnext.set.map.js')
|
|
18
|
+
require('core-js/modules/esnext.set.reduce.js')
|
|
19
|
+
require('core-js/modules/esnext.set.some.js')
|
|
20
|
+
require('core-js/modules/esnext.set.symmetric-difference.js')
|
|
21
|
+
require('core-js/modules/esnext.set.union.js')
|
|
22
|
+
Object.defineProperty(exports, '__esModule', {
|
|
23
|
+
value: true
|
|
24
|
+
})
|
|
25
|
+
exports.prettyPrint = exports.serializeOuter = exports.serializeChildren = void 0
|
|
26
|
+
/**
|
|
27
|
+
* @file Serializes pseudo-dom nodes back into an HTML string, for innerHTML / outerHTML.
|
|
28
|
+
* @author Joshua Heagle <joshuaheagle@gmail.com>
|
|
29
|
+
* @version 1.0.0
|
|
30
|
+
*/
|
|
31
|
+
const NodeService_1 = require('../services/NodeService')
|
|
32
|
+
// Void elements are self-closing and never serialize a closing tag or children (there is no HTML parsing of their
|
|
33
|
+
// content either - the DOM never lets them have children in the first place).
|
|
34
|
+
const VOID_ELEMENTS = new Set(['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr'])
|
|
35
|
+
// These property-backed names (see ElementService / HTMLElementService's constructors) are not real HTML content
|
|
36
|
+
// attributes - offsetWidth and friends describe layout, which does not exist here, and innerHTML is JS-only - so
|
|
37
|
+
// they must never appear in serialized markup, however getAttributeNames() may report them.
|
|
38
|
+
const NEVER_SERIALIZE = new Set(['innerHTML', 'clientHeight', 'clientLeft', 'clientTop', 'clientWidth', 'scrollHeight', 'scrollLeft', 'scrollTop', 'scrollWidth', 'offsetHeight', 'offsetLeft', 'offsetParent', 'offsetTop', 'offsetWidth'])
|
|
39
|
+
// The attribute name to serialize a property-backed name as, when it differs from the property name
|
|
40
|
+
const ATTRIBUTE_NAME = {
|
|
41
|
+
className: 'class'
|
|
42
|
+
}
|
|
43
|
+
// Boolean HTML attributes: present (with no value) when true, absent entirely when false
|
|
44
|
+
const BOOLEAN_ATTRIBUTES = new Set(['hidden'])
|
|
45
|
+
/**
|
|
46
|
+
* Escape text so it is safe inside HTML text content. Coerces to a string first - unlike a real DOM, pseudo-dom's
|
|
47
|
+
* setAttribute does not itself coerce (see the same note on escapeAttributeValue), and nodeValue is not guaranteed
|
|
48
|
+
* to be a string either.
|
|
49
|
+
* @param {*} text
|
|
50
|
+
* @returns {string}
|
|
51
|
+
*/
|
|
52
|
+
const escapeText = text => String(text).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
|
53
|
+
/**
|
|
54
|
+
* Escape a value so it is safe inside a double-quoted HTML attribute. Coerces to a string first: a real DOM's
|
|
55
|
+
* setAttribute always stores a string, however pseudo-dom's does not coerce what it is given, so a value set via
|
|
56
|
+
* setAttribute(name, 5) is stored (and read back by getAttribute) as the number 5, not the string '5'.
|
|
57
|
+
* @param {*} value
|
|
58
|
+
* @returns {string}
|
|
59
|
+
*/
|
|
60
|
+
const escapeAttributeValue = value => String(value).replace(/&/g, '&').replace(/"/g, '"')
|
|
61
|
+
/**
|
|
62
|
+
* Every attribute of the element, serialized (class instead of className, boolean attributes bare, the never-real
|
|
63
|
+
* mock properties left out, style added from the live CSSStyleDeclaration when it is not empty).
|
|
64
|
+
* @param {*} element
|
|
65
|
+
* @returns {string}
|
|
66
|
+
*/
|
|
67
|
+
const serializeAttributes = element => {
|
|
68
|
+
let result = (element.getAttributeNames ? element.getAttributeNames() : []).filter(name => !NEVER_SERIALIZE.has(name)).map(name => {
|
|
69
|
+
if (BOOLEAN_ATTRIBUTES.has(name)) {
|
|
70
|
+
return element[name] ? ` ${name}` : ''
|
|
71
|
+
}
|
|
72
|
+
const value = element.getAttribute(name)
|
|
73
|
+
return value === '' ? '' : ` ${ATTRIBUTE_NAME[name] || name}="${escapeAttributeValue(value)}"`
|
|
74
|
+
}).join('')
|
|
75
|
+
const style = element.style
|
|
76
|
+
if (style && style.cssText) {
|
|
77
|
+
result += ` style="${escapeAttributeValue(style.cssText)}"`
|
|
78
|
+
}
|
|
79
|
+
return result
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* One node, serialized (its own markup only - see serializeChildren for its descendants too).
|
|
83
|
+
* @param {*} node
|
|
84
|
+
* @returns {string}
|
|
85
|
+
*/
|
|
86
|
+
const serializeNode = node => {
|
|
87
|
+
if (node.nodeType === NodeService_1.NodeService.TEXT_NODE) {
|
|
88
|
+
return escapeText(node.nodeValue || '')
|
|
89
|
+
}
|
|
90
|
+
if (node.nodeType === NodeService_1.NodeService.COMMENT_NODE) {
|
|
91
|
+
return `<!--${node.nodeValue || ''}-->`
|
|
92
|
+
}
|
|
93
|
+
if (node.nodeType !== NodeService_1.NodeService.ELEMENT_NODE) {
|
|
94
|
+
return ''
|
|
95
|
+
}
|
|
96
|
+
const tagName = node.tagName
|
|
97
|
+
const attributes = serializeAttributes(node)
|
|
98
|
+
if (VOID_ELEMENTS.has(tagName)) {
|
|
99
|
+
return `<${tagName}${attributes}>`
|
|
100
|
+
}
|
|
101
|
+
return `<${tagName}${attributes}>${serializeChildren(node)}</${tagName}>`
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* A node's children, serialized in order (this is what innerHTML returns).
|
|
105
|
+
* @memberOf module:factories
|
|
106
|
+
* @param {*} node
|
|
107
|
+
* @returns {string}
|
|
108
|
+
*/
|
|
109
|
+
const serializeChildren = node => Array.from(node.childNodes).map(serializeNode).join('')
|
|
110
|
+
exports.serializeChildren = serializeChildren
|
|
111
|
+
/**
|
|
112
|
+
* An element itself, serialized with its children (this is what outerHTML returns).
|
|
113
|
+
* @memberOf module:factories
|
|
114
|
+
* @param {*} element
|
|
115
|
+
* @returns {string}
|
|
116
|
+
*/
|
|
117
|
+
const serializeOuter = element => serializeNode(element)
|
|
118
|
+
exports.serializeOuter = serializeOuter
|
|
119
|
+
/**
|
|
120
|
+
* One node, indented for readability (see prettyPrint) - unlike serializeNode, every non-empty node is its own
|
|
121
|
+
* line, so the structure of a whole tree is easy to read at a glance.
|
|
122
|
+
* @param {*} node
|
|
123
|
+
* @param {number} depth
|
|
124
|
+
* @param {string} indent
|
|
125
|
+
* @returns {string}
|
|
126
|
+
*/
|
|
127
|
+
const prettyPrintNode = (node, depth, indent) => {
|
|
128
|
+
const pad = indent.repeat(depth)
|
|
129
|
+
if (node.nodeType === NodeService_1.NodeService.TEXT_NODE) {
|
|
130
|
+
const text = escapeText((node.nodeValue || '').trim())
|
|
131
|
+
return text ? `${pad}${text}` : ''
|
|
132
|
+
}
|
|
133
|
+
if (node.nodeType === NodeService_1.NodeService.COMMENT_NODE) {
|
|
134
|
+
return `${pad}<!--${node.nodeValue || ''}-->`
|
|
135
|
+
}
|
|
136
|
+
if (node.nodeType !== NodeService_1.NodeService.ELEMENT_NODE) {
|
|
137
|
+
return ''
|
|
138
|
+
}
|
|
139
|
+
const tagName = node.tagName
|
|
140
|
+
const attributes = serializeAttributes(node)
|
|
141
|
+
if (VOID_ELEMENTS.has(tagName)) {
|
|
142
|
+
return `${pad}<${tagName}${attributes}>`
|
|
143
|
+
}
|
|
144
|
+
const children = Array.from(node.childNodes).map(child => prettyPrintNode(child, depth + 1, indent)).filter(line => line !== '')
|
|
145
|
+
if (children.length === 0) {
|
|
146
|
+
return `${pad}<${tagName}${attributes}></${tagName}>`
|
|
147
|
+
}
|
|
148
|
+
return `${pad}<${tagName}${attributes}>\n${children.join('\n')}\n${pad}</${tagName}>`
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* A node's markup, indented one level per level of nesting - a real DOM's outerHTML / innerHTML has no line breaks
|
|
152
|
+
* at all, which does not read well for a whole tree (a board full of cells, say). Useful for watching pseudo-dom-
|
|
153
|
+
* driven code run headlessly, printed to a terminal - see also logElement, which does the printing too.
|
|
154
|
+
* @memberOf module:factories
|
|
155
|
+
* @param {*} node
|
|
156
|
+
* @param {string} [indent=' '] The indentation used per level of nesting
|
|
157
|
+
* @returns {string}
|
|
158
|
+
*/
|
|
159
|
+
const prettyPrint = (node, indent = ' ') => prettyPrintNode(node, 0, indent)
|
|
160
|
+
exports.prettyPrint = prettyPrint
|