pseudo-dom 0.10.0 → 0.12.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 +313 -84
- package/browser/pseudo-dom.js +3270 -538
- package/browser/pseudo-dom.min.js +1 -1
- package/dist/factories/generateDocument.js +70 -65
- package/dist/factories/generateDocument.min.js +1 -1
- package/dist/factories/installGlobal.d.ts +14 -0
- package/dist/factories/installGlobal.js +40 -0
- package/dist/factories/installGlobal.min.js +1 -0
- package/dist/factories/logElement.d.ts +9 -0
- package/dist/factories/logElement.js +23 -0
- package/dist/factories/logElement.min.js +1 -0
- package/dist/factories/parseHTML.d.ts +13 -0
- package/dist/factories/parseHTML.js +84 -0
- package/dist/factories/parseHTML.min.js +1 -0
- package/dist/factories/serializeHTML.d.ts +25 -0
- package/dist/factories/serializeHTML.js +156 -0
- package/dist/factories/serializeHTML.min.js +1 -0
- package/dist/interfaces/PseudoElement.d.ts +765 -765
- package/dist/interfaces/PseudoShadowRoot.d.ts +12 -12
- package/dist/main.d.ts +69 -63
- package/dist/main.js +169 -155
- package/dist/main.min.js +1 -1
- package/dist/services/ElementService.d.ts +371 -361
- package/dist/services/ElementService.js +732 -717
- package/dist/services/ElementService.min.js +1 -1
- package/dist/services/HTMLElementService.d.ts +32 -0
- package/dist/services/HTMLElementService.js +67 -0
- package/dist/services/HTMLElementService.min.js +1 -1
- package/dist/services/NodeService.d.ts +305 -305
- package/dist/services/NodeService.js +732 -732
- package/dist/services/ShadowRootService.d.ts +16 -16
- package/dist/services/ShadowRootService.js +24 -24
- package/package.json +4 -2
|
@@ -1,65 +1,70 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const __importDefault = void 0 && (void 0).__importDefault || function (mod) {
|
|
4
|
-
return mod && mod.__esModule
|
|
5
|
-
? mod
|
|
6
|
-
: {
|
|
7
|
-
default: mod
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
Object.defineProperty(exports, '__esModule', {
|
|
11
|
-
value: true
|
|
12
|
-
})
|
|
13
|
-
const EventTargetService_1 = __importDefault(require('../services/EventTargetService'))
|
|
14
|
-
const NodeService_1 = require('../services/NodeService')
|
|
15
|
-
const ElementService_1 = require('../services/ElementService')
|
|
16
|
-
const HTMLElementService_1 = require('../services/HTMLElementService')
|
|
17
|
-
const PseudoHTMLDocument_1 = __importDefault(require('../classes/PseudoHTMLDocument'))
|
|
18
|
-
/**
|
|
19
|
-
* Construct the Pseudo Dom to provide access to Dom objects which are otherwise not available outside the browser
|
|
20
|
-
* context.
|
|
21
|
-
* @function generateDocument
|
|
22
|
-
* @param {Object} root
|
|
23
|
-
* @param {Object} context
|
|
24
|
-
* @returns {Window|PseudoEventTarget}
|
|
25
|
-
*/
|
|
26
|
-
const generateDocument = (root, context = {}) => {
|
|
27
|
-
/**
|
|
28
|
-
*
|
|
29
|
-
* @type {Window|PseudoEventTarget}
|
|
30
|
-
*/
|
|
31
|
-
const newWindow = typeof root.document === 'undefined' ? root : new EventTargetService_1.default()
|
|
32
|
-
/**
|
|
33
|
-
* @type {Node|PseudoNode}
|
|
34
|
-
*/
|
|
35
|
-
const Node = root.Node || new NodeService_1.NodeService()
|
|
36
|
-
if (typeof newWindow.Node === 'undefined') {
|
|
37
|
-
newWindow.Node = Node
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
*
|
|
41
|
-
* @type {Element|PseudoElement}
|
|
42
|
-
*/
|
|
43
|
-
const Element = root.Element || new ElementService_1.ElementService()
|
|
44
|
-
if (typeof newWindow.Element === 'undefined') {
|
|
45
|
-
newWindow.Element = Element
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Create an instance of HTMLElement if not available
|
|
49
|
-
* @type {HTMLElement|PseudoHTMLElement}
|
|
50
|
-
*/
|
|
51
|
-
const HTMLElement = root.HTMLElement || new HTMLElementService_1.HTMLElementService()
|
|
52
|
-
if (typeof newWindow.HTMLElement === 'undefined') {
|
|
53
|
-
newWindow.HTMLElement = HTMLElement
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Define document when not available
|
|
57
|
-
* @type {Document|PseudoHTMLDocument}
|
|
58
|
-
*/
|
|
59
|
-
const document = root.document || new PseudoHTMLDocument_1.default()
|
|
60
|
-
if (typeof newWindow.document === 'undefined') {
|
|
61
|
-
newWindow.document = document
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const __importDefault = void 0 && (void 0).__importDefault || function (mod) {
|
|
4
|
+
return mod && mod.__esModule
|
|
5
|
+
? mod
|
|
6
|
+
: {
|
|
7
|
+
default: mod
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
Object.defineProperty(exports, '__esModule', {
|
|
11
|
+
value: true
|
|
12
|
+
})
|
|
13
|
+
const EventTargetService_1 = __importDefault(require('../services/EventTargetService'))
|
|
14
|
+
const NodeService_1 = require('../services/NodeService')
|
|
15
|
+
const ElementService_1 = require('../services/ElementService')
|
|
16
|
+
const HTMLElementService_1 = require('../services/HTMLElementService')
|
|
17
|
+
const PseudoHTMLDocument_1 = __importDefault(require('../classes/PseudoHTMLDocument'))
|
|
18
|
+
/**
|
|
19
|
+
* Construct the Pseudo Dom to provide access to Dom objects which are otherwise not available outside the browser
|
|
20
|
+
* context.
|
|
21
|
+
* @function generateDocument
|
|
22
|
+
* @param {Object} root
|
|
23
|
+
* @param {Object} context
|
|
24
|
+
* @returns {Window|PseudoEventTarget}
|
|
25
|
+
*/
|
|
26
|
+
const generateDocument = (root, context = {}) => {
|
|
27
|
+
/**
|
|
28
|
+
*
|
|
29
|
+
* @type {Window|PseudoEventTarget}
|
|
30
|
+
*/
|
|
31
|
+
const newWindow = typeof root.document === 'undefined' ? root : new EventTargetService_1.default()
|
|
32
|
+
/**
|
|
33
|
+
* @type {Node|PseudoNode}
|
|
34
|
+
*/
|
|
35
|
+
const Node = root.Node || new NodeService_1.NodeService()
|
|
36
|
+
if (typeof newWindow.Node === 'undefined') {
|
|
37
|
+
newWindow.Node = Node
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
*
|
|
41
|
+
* @type {Element|PseudoElement}
|
|
42
|
+
*/
|
|
43
|
+
const Element = root.Element || new ElementService_1.ElementService()
|
|
44
|
+
if (typeof newWindow.Element === 'undefined') {
|
|
45
|
+
newWindow.Element = Element
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Create an instance of HTMLElement if not available
|
|
49
|
+
* @type {HTMLElement|PseudoHTMLElement}
|
|
50
|
+
*/
|
|
51
|
+
const HTMLElement = root.HTMLElement || new HTMLElementService_1.HTMLElementService()
|
|
52
|
+
if (typeof newWindow.HTMLElement === 'undefined') {
|
|
53
|
+
newWindow.HTMLElement = HTMLElement
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Define document when not available
|
|
57
|
+
* @type {Document|PseudoHTMLDocument}
|
|
58
|
+
*/
|
|
59
|
+
const document = root.document || new PseudoHTMLDocument_1.default()
|
|
60
|
+
if (typeof newWindow.document === 'undefined') {
|
|
61
|
+
newWindow.document = document
|
|
62
|
+
}
|
|
63
|
+
// When there was no real document, newWindow is root itself (see above), so every assignment above already
|
|
64
|
+
// mutated it directly. If context is also that same object (root and context given as one and the same, as
|
|
65
|
+
// installGlobal does) merging is already done - doing it again would be Object.assign(target, target), which
|
|
66
|
+
// throws on any getter-only own property target already has (globalThis.crypto, in Node).
|
|
67
|
+
const mergeInto = context || root
|
|
68
|
+
return newWindow === mergeInto ? newWindow : Object.assign(mergeInto, newWindow)
|
|
69
|
+
}
|
|
70
|
+
exports.default = generateDocument
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var __importDefault=function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(exports,"__esModule",{value:!0});const EventTargetService_1=__importDefault(require("../services/EventTargetService")),NodeService_1=require("../services/NodeService"),ElementService_1=require("../services/ElementService"),HTMLElementService_1=require("../services/HTMLElementService"),PseudoHTMLDocument_1=__importDefault(require("../classes/PseudoHTMLDocument")),generateDocument=(e,t={})=>{const r=void 0===e.document?e:new EventTargetService_1.default,n=e.Node||new NodeService_1.NodeService;void 0===r.Node&&(r.Node=n);const c=e.Element||new ElementService_1.ElementService;void 0===r.Element&&(r.Element=c);const o=e.HTMLElement||new HTMLElementService_1.HTMLElementService;void 0===r.HTMLElement&&(r.HTMLElement=o);const i=e.document||new PseudoHTMLDocument_1.default;
|
|
1
|
+
"use strict";var __importDefault=function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(exports,"__esModule",{value:!0});const EventTargetService_1=__importDefault(require("../services/EventTargetService")),NodeService_1=require("../services/NodeService"),ElementService_1=require("../services/ElementService"),HTMLElementService_1=require("../services/HTMLElementService"),PseudoHTMLDocument_1=__importDefault(require("../classes/PseudoHTMLDocument")),generateDocument=(e,t={})=>{const r=void 0===e.document?e:new EventTargetService_1.default,n=e.Node||new NodeService_1.NodeService;void 0===r.Node&&(r.Node=n);const c=e.Element||new ElementService_1.ElementService;void 0===r.Element&&(r.Element=c);const o=e.HTMLElement||new HTMLElementService_1.HTMLElementService;void 0===r.HTMLElement&&(r.HTMLElement=o);const i=e.document||new PseudoHTMLDocument_1.default;void 0===r.document&&(r.document=i);const u=t||e;return r===u?r:Object.assign(u,r)};exports.default=generateDocument;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* When there is no real DOM already available (a real browser, or a jsdom-based test environment - isBrowser is
|
|
3
|
+
* true for both, since jsdom's window has a document too, and a jsdom document should never be overwritten, real
|
|
4
|
+
* DOM behaviour is always closer to the real thing), populate the given global-like target with document / Node /
|
|
5
|
+
* Element / HTMLElement, generated by generateDocument, so code written against a real DOM's globals works as-is.
|
|
6
|
+
* Does nothing when a real DOM is already there, matching generateDocument's own only-fill-what-is-missing
|
|
7
|
+
* behaviour - calling this is always safe, in any environment.
|
|
8
|
+
* @memberOf module:factories
|
|
9
|
+
* @param {*} [target] The object to add document / Node / Element / HTMLElement onto - defaults to globalThis, so
|
|
10
|
+
* bare `document`, `Node`, etc. resolve from anywhere once this has run
|
|
11
|
+
* @returns {*} The same target, for convenience
|
|
12
|
+
*/
|
|
13
|
+
declare const installGlobal: (target?: any) => any;
|
|
14
|
+
export default installGlobal;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const __importDefault = void 0 && (void 0).__importDefault || function (mod) {
|
|
4
|
+
return mod && mod.__esModule
|
|
5
|
+
? mod
|
|
6
|
+
: {
|
|
7
|
+
default: mod
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
Object.defineProperty(exports, '__esModule', {
|
|
11
|
+
value: true
|
|
12
|
+
})
|
|
13
|
+
/**
|
|
14
|
+
* @file Makes pseudo-dom a drop-in swap for a real DOM: code written against bare globals (document.createElement,
|
|
15
|
+
* new Node(), ...) works unmodified, in the browser or in Node, without an if (typeof document === 'undefined')
|
|
16
|
+
* check at every call site.
|
|
17
|
+
* @author Joshua Heagle <joshuaheagle@gmail.com>
|
|
18
|
+
* @version 1.0.0
|
|
19
|
+
*/
|
|
20
|
+
const browser_or_node_1 = require('browser-or-node')
|
|
21
|
+
const generateDocument_1 = __importDefault(require('./generateDocument'))
|
|
22
|
+
/**
|
|
23
|
+
* When there is no real DOM already available (a real browser, or a jsdom-based test environment - isBrowser is
|
|
24
|
+
* true for both, since jsdom's window has a document too, and a jsdom document should never be overwritten, real
|
|
25
|
+
* DOM behaviour is always closer to the real thing), populate the given global-like target with document / Node /
|
|
26
|
+
* Element / HTMLElement, generated by generateDocument, so code written against a real DOM's globals works as-is.
|
|
27
|
+
* Does nothing when a real DOM is already there, matching generateDocument's own only-fill-what-is-missing
|
|
28
|
+
* behaviour - calling this is always safe, in any environment.
|
|
29
|
+
* @memberOf module:factories
|
|
30
|
+
* @param {*} [target] The object to add document / Node / Element / HTMLElement onto - defaults to globalThis, so
|
|
31
|
+
* bare `document`, `Node`, etc. resolve from anywhere once this has run
|
|
32
|
+
* @returns {*} The same target, for convenience
|
|
33
|
+
*/
|
|
34
|
+
const installGlobal = (target = typeof globalThis !== 'undefined' ? globalThis : {}) => {
|
|
35
|
+
if (!browser_or_node_1.isBrowser) {
|
|
36
|
+
(0, generateDocument_1.default)(target, target)
|
|
37
|
+
}
|
|
38
|
+
return target
|
|
39
|
+
}
|
|
40
|
+
exports.default = installGlobal
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var __importDefault=function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(exports,"__esModule",{value:!0});const browser_or_node_1=require("browser-or-node"),generateDocument_1=__importDefault(require("./generateDocument")),installGlobal=(e=("undefined"!=typeof globalThis?globalThis:{}))=>(browser_or_node_1.isBrowser||(0,generateDocument_1.default)(e,e),e);exports.default=installGlobal;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Print a node's markup to the console, indented for readability (see prettyPrint).
|
|
3
|
+
* @memberOf module:factories
|
|
4
|
+
* @param {*} node
|
|
5
|
+
* @param {string} [label=''] A label printed above the markup, to identify this log call
|
|
6
|
+
* @returns {undefined}
|
|
7
|
+
*/
|
|
8
|
+
declare const logElement: (node: any, label?: string) => void;
|
|
9
|
+
export default logElement;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', {
|
|
4
|
+
value: true
|
|
5
|
+
})
|
|
6
|
+
/**
|
|
7
|
+
* @file Prints an element (or any node) to the console, readably - the point of pseudo-dom running headlessly is
|
|
8
|
+
* usually to watch what code does to a tree without a real browser to look at.
|
|
9
|
+
* @author Joshua Heagle <joshuaheagle@gmail.com>
|
|
10
|
+
* @version 1.0.0
|
|
11
|
+
*/
|
|
12
|
+
const serializeHTML_1 = require('./serializeHTML')
|
|
13
|
+
/**
|
|
14
|
+
* Print a node's markup to the console, indented for readability (see prettyPrint).
|
|
15
|
+
* @memberOf module:factories
|
|
16
|
+
* @param {*} node
|
|
17
|
+
* @param {string} [label=''] A label printed above the markup, to identify this log call
|
|
18
|
+
* @returns {undefined}
|
|
19
|
+
*/
|
|
20
|
+
const logElement = (node, label = '') => {
|
|
21
|
+
console.log(`${label ? `${label}\n` : ''}${(0, serializeHTML_1.prettyPrint)(node)}`)
|
|
22
|
+
}
|
|
23
|
+
exports.default = logElement
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});const serializeHTML_1=require("./serializeHTML"),logElement=(e,t="")=>{console.log(`${t?`${t}\n`:""}${(0,serializeHTML_1.prettyPrint)(e)}`)};exports.default=logElement;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse an HTML string into the nodes it describes (siblings at the top level, exactly like the DOM's own HTML
|
|
3
|
+
* parsing does for innerHTML / insertAdjacentHTML - there is no single root unless the markup itself has one).
|
|
4
|
+
* @memberOf module:factories
|
|
5
|
+
* @param {string} html
|
|
6
|
+
* @param {*} ownerDocument The document the new nodes belong to (matches what innerHTML etc. would set), or null
|
|
7
|
+
* @param {function(new: *, {tagName: string})} ElementClass The class to build each parsed element with
|
|
8
|
+
* @returns {Array<*>}
|
|
9
|
+
*/
|
|
10
|
+
declare const parseHTML: (html: string, ownerDocument: any, ElementClass: new (options: {
|
|
11
|
+
tagName: string;
|
|
12
|
+
}) => any) => Array<any>;
|
|
13
|
+
export default parseHTML;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', {
|
|
4
|
+
value: true
|
|
5
|
+
})
|
|
6
|
+
/**
|
|
7
|
+
* @file Parses an HTML string into pseudo-dom nodes, for innerHTML / outerHTML / insertAdjacentHTML. Built on
|
|
8
|
+
* htmlparser2's Parser (a SAX-style tokenizer) with a custom handler which builds pseudo-dom nodes directly -
|
|
9
|
+
* htmlparser2's own default DomHandler / domutils tree (which this never uses) is stubbed out of the browser bundle
|
|
10
|
+
* by the browser.ignore config, the same way css-select's unused default adapter already is.
|
|
11
|
+
*
|
|
12
|
+
* The class to build parsed elements with is given by the caller (rather than imported here) so this has no
|
|
13
|
+
* dependency on ElementService / HTMLElementService - importing either here, from a factory ElementService itself
|
|
14
|
+
* would need to call, would create an import cycle.
|
|
15
|
+
* @author Joshua Heagle <joshuaheagle@gmail.com>
|
|
16
|
+
* @version 1.0.0
|
|
17
|
+
*/
|
|
18
|
+
const htmlparser2_1 = require('htmlparser2')
|
|
19
|
+
const NodeService_1 = require('../services/NodeService')
|
|
20
|
+
/**
|
|
21
|
+
* Parse an HTML string into the nodes it describes (siblings at the top level, exactly like the DOM's own HTML
|
|
22
|
+
* parsing does for innerHTML / insertAdjacentHTML - there is no single root unless the markup itself has one).
|
|
23
|
+
* @memberOf module:factories
|
|
24
|
+
* @param {string} html
|
|
25
|
+
* @param {*} ownerDocument The document the new nodes belong to (matches what innerHTML etc. would set), or null
|
|
26
|
+
* @param {function(new: *, {tagName: string})} ElementClass The class to build each parsed element with
|
|
27
|
+
* @returns {Array<*>}
|
|
28
|
+
*/
|
|
29
|
+
const parseHTML = (html, ownerDocument, ElementClass) => {
|
|
30
|
+
const roots = []
|
|
31
|
+
const stack = []
|
|
32
|
+
const appendNode = node => {
|
|
33
|
+
node.ownerDocumentStore = ownerDocument
|
|
34
|
+
if (stack.length) {
|
|
35
|
+
stack[stack.length - 1].appendChild(node)
|
|
36
|
+
} else {
|
|
37
|
+
roots.push(node)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const parser = new htmlparser2_1.Parser({
|
|
41
|
+
onopentagname (name) {
|
|
42
|
+
const element = new ElementClass({
|
|
43
|
+
tagName: name
|
|
44
|
+
})
|
|
45
|
+
appendNode(element)
|
|
46
|
+
stack.push(element)
|
|
47
|
+
},
|
|
48
|
+
onattribute (name, value) {
|
|
49
|
+
if (!stack.length) {
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
const element = stack[stack.length - 1]
|
|
53
|
+
// pseudo-dom's className / classList and style are not kept in sync with a literal 'class' / 'style'
|
|
54
|
+
// attribute the way setAttribute usually is (there is no such special-casing here, unlike a real DOM) - route
|
|
55
|
+
// them to the real objects directly, the same way cssSelectAdapter does for reading 'class' when matching a
|
|
56
|
+
// CSS selector, so parsed markup's class="..." / style="..." actually populates className/classList and style.
|
|
57
|
+
if (name === 'class') {
|
|
58
|
+
element.className = value
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
if (name === 'style' && element.style) {
|
|
62
|
+
element.style.cssText = value
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
element.setAttribute(name, value)
|
|
66
|
+
},
|
|
67
|
+
ontext (text) {
|
|
68
|
+
appendNode(new NodeService_1.TextService(text))
|
|
69
|
+
},
|
|
70
|
+
oncomment (data) {
|
|
71
|
+
appendNode(new NodeService_1.CommentService(data))
|
|
72
|
+
},
|
|
73
|
+
onclosetag () {
|
|
74
|
+
stack.pop()
|
|
75
|
+
}
|
|
76
|
+
}, {
|
|
77
|
+
lowerCaseTags: true,
|
|
78
|
+
lowerCaseAttributeNames: true
|
|
79
|
+
})
|
|
80
|
+
parser.write(String(html || ''))
|
|
81
|
+
parser.end()
|
|
82
|
+
return roots
|
|
83
|
+
}
|
|
84
|
+
exports.default = parseHTML
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});const htmlparser2_1=require("htmlparser2"),NodeService_1=require("../services/NodeService"),parseHTML=(e,t,r)=>{const s=[],n=[],o=e=>{e.ownerDocumentStore=t,n.length?n[n.length-1].appendChild(e):s.push(e)},a=new htmlparser2_1.Parser({onopentagname(e){const t=new r({tagName:e});o(t),n.push(t)},onattribute(e,t){if(!n.length)return;const r=n[n.length-1];"class"!==e?"style"===e&&r.style?r.style.cssText=t:r.setAttribute(e,t):r.className=t},ontext(e){o(new NodeService_1.TextService(e))},oncomment(e){o(new NodeService_1.CommentService(e))},onclosetag(){n.pop()}},{lowerCaseTags:!0,lowerCaseAttributeNames:!0});return a.write(String(e||"")),a.end(),s};exports.default=parseHTML;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A node's children, serialized in order (this is what innerHTML returns).
|
|
3
|
+
* @memberOf module:factories
|
|
4
|
+
* @param {*} node
|
|
5
|
+
* @returns {string}
|
|
6
|
+
*/
|
|
7
|
+
declare const serializeChildren: (node: any) => string;
|
|
8
|
+
/**
|
|
9
|
+
* An element itself, serialized with its children (this is what outerHTML returns).
|
|
10
|
+
* @memberOf module:factories
|
|
11
|
+
* @param {*} element
|
|
12
|
+
* @returns {string}
|
|
13
|
+
*/
|
|
14
|
+
declare const serializeOuter: (element: any) => string;
|
|
15
|
+
/**
|
|
16
|
+
* A node's markup, indented one level per level of nesting - a real DOM's outerHTML / innerHTML has no line breaks
|
|
17
|
+
* at all, which does not read well for a whole tree (a board full of cells, say). Useful for watching pseudo-dom-
|
|
18
|
+
* driven code run headlessly, printed to a terminal - see also logElement, which does the printing too.
|
|
19
|
+
* @memberOf module:factories
|
|
20
|
+
* @param {*} node
|
|
21
|
+
* @param {string} [indent=' '] The indentation used per level of nesting
|
|
22
|
+
* @returns {string}
|
|
23
|
+
*/
|
|
24
|
+
declare const prettyPrint: (node: any, indent?: string) => string;
|
|
25
|
+
export { serializeChildren, serializeOuter, prettyPrint };
|
|
@@ -0,0 +1,156 @@
|
|
|
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.
|
|
47
|
+
* @param {string} text
|
|
48
|
+
* @returns {string}
|
|
49
|
+
*/
|
|
50
|
+
const escapeText = text => text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
|
51
|
+
/**
|
|
52
|
+
* Escape a value so it is safe inside a double-quoted HTML attribute.
|
|
53
|
+
* @param {string} value
|
|
54
|
+
* @returns {string}
|
|
55
|
+
*/
|
|
56
|
+
const escapeAttributeValue = value => value.replace(/&/g, '&').replace(/"/g, '"')
|
|
57
|
+
/**
|
|
58
|
+
* Every attribute of the element, serialized (class instead of className, boolean attributes bare, the never-real
|
|
59
|
+
* mock properties left out, style added from the live CSSStyleDeclaration when it is not empty).
|
|
60
|
+
* @param {*} element
|
|
61
|
+
* @returns {string}
|
|
62
|
+
*/
|
|
63
|
+
const serializeAttributes = element => {
|
|
64
|
+
let result = (element.getAttributeNames ? element.getAttributeNames() : []).filter(name => !NEVER_SERIALIZE.has(name)).map(name => {
|
|
65
|
+
if (BOOLEAN_ATTRIBUTES.has(name)) {
|
|
66
|
+
return element[name] ? ` ${name}` : ''
|
|
67
|
+
}
|
|
68
|
+
const value = element.getAttribute(name)
|
|
69
|
+
return value === '' ? '' : ` ${ATTRIBUTE_NAME[name] || name}="${escapeAttributeValue(value)}"`
|
|
70
|
+
}).join('')
|
|
71
|
+
const style = element.style
|
|
72
|
+
if (style && style.cssText) {
|
|
73
|
+
result += ` style="${escapeAttributeValue(style.cssText)}"`
|
|
74
|
+
}
|
|
75
|
+
return result
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* One node, serialized (its own markup only - see serializeChildren for its descendants too).
|
|
79
|
+
* @param {*} node
|
|
80
|
+
* @returns {string}
|
|
81
|
+
*/
|
|
82
|
+
const serializeNode = node => {
|
|
83
|
+
if (node.nodeType === NodeService_1.NodeService.TEXT_NODE) {
|
|
84
|
+
return escapeText(node.nodeValue || '')
|
|
85
|
+
}
|
|
86
|
+
if (node.nodeType === NodeService_1.NodeService.COMMENT_NODE) {
|
|
87
|
+
return `<!--${node.nodeValue || ''}-->`
|
|
88
|
+
}
|
|
89
|
+
if (node.nodeType !== NodeService_1.NodeService.ELEMENT_NODE) {
|
|
90
|
+
return ''
|
|
91
|
+
}
|
|
92
|
+
const tagName = node.tagName
|
|
93
|
+
const attributes = serializeAttributes(node)
|
|
94
|
+
if (VOID_ELEMENTS.has(tagName)) {
|
|
95
|
+
return `<${tagName}${attributes}>`
|
|
96
|
+
}
|
|
97
|
+
return `<${tagName}${attributes}>${serializeChildren(node)}</${tagName}>`
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* A node's children, serialized in order (this is what innerHTML returns).
|
|
101
|
+
* @memberOf module:factories
|
|
102
|
+
* @param {*} node
|
|
103
|
+
* @returns {string}
|
|
104
|
+
*/
|
|
105
|
+
const serializeChildren = node => Array.from(node.childNodes).map(serializeNode).join('')
|
|
106
|
+
exports.serializeChildren = serializeChildren
|
|
107
|
+
/**
|
|
108
|
+
* An element itself, serialized with its children (this is what outerHTML returns).
|
|
109
|
+
* @memberOf module:factories
|
|
110
|
+
* @param {*} element
|
|
111
|
+
* @returns {string}
|
|
112
|
+
*/
|
|
113
|
+
const serializeOuter = element => serializeNode(element)
|
|
114
|
+
exports.serializeOuter = serializeOuter
|
|
115
|
+
/**
|
|
116
|
+
* One node, indented for readability (see prettyPrint) - unlike serializeNode, every non-empty node is its own
|
|
117
|
+
* line, so the structure of a whole tree is easy to read at a glance.
|
|
118
|
+
* @param {*} node
|
|
119
|
+
* @param {number} depth
|
|
120
|
+
* @param {string} indent
|
|
121
|
+
* @returns {string}
|
|
122
|
+
*/
|
|
123
|
+
const prettyPrintNode = (node, depth, indent) => {
|
|
124
|
+
const pad = indent.repeat(depth)
|
|
125
|
+
if (node.nodeType === NodeService_1.NodeService.TEXT_NODE) {
|
|
126
|
+
const text = escapeText((node.nodeValue || '').trim())
|
|
127
|
+
return text ? `${pad}${text}` : ''
|
|
128
|
+
}
|
|
129
|
+
if (node.nodeType === NodeService_1.NodeService.COMMENT_NODE) {
|
|
130
|
+
return `${pad}<!--${node.nodeValue || ''}-->`
|
|
131
|
+
}
|
|
132
|
+
if (node.nodeType !== NodeService_1.NodeService.ELEMENT_NODE) {
|
|
133
|
+
return ''
|
|
134
|
+
}
|
|
135
|
+
const tagName = node.tagName
|
|
136
|
+
const attributes = serializeAttributes(node)
|
|
137
|
+
if (VOID_ELEMENTS.has(tagName)) {
|
|
138
|
+
return `${pad}<${tagName}${attributes}>`
|
|
139
|
+
}
|
|
140
|
+
const children = Array.from(node.childNodes).map(child => prettyPrintNode(child, depth + 1, indent)).filter(line => line !== '')
|
|
141
|
+
if (children.length === 0) {
|
|
142
|
+
return `${pad}<${tagName}${attributes}></${tagName}>`
|
|
143
|
+
}
|
|
144
|
+
return `${pad}<${tagName}${attributes}>\n${children.join('\n')}\n${pad}</${tagName}>`
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* A node's markup, indented one level per level of nesting - a real DOM's outerHTML / innerHTML has no line breaks
|
|
148
|
+
* at all, which does not read well for a whole tree (a board full of cells, say). Useful for watching pseudo-dom-
|
|
149
|
+
* driven code run headlessly, printed to a terminal - see also logElement, which does the printing too.
|
|
150
|
+
* @memberOf module:factories
|
|
151
|
+
* @param {*} node
|
|
152
|
+
* @param {string} [indent=' '] The indentation used per level of nesting
|
|
153
|
+
* @returns {string}
|
|
154
|
+
*/
|
|
155
|
+
const prettyPrint = (node, indent = ' ') => prettyPrintNode(node, 0, indent)
|
|
156
|
+
exports.prettyPrint = prettyPrint
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";require("core-js/modules/esnext.iterator.constructor.js"),require("core-js/modules/esnext.iterator.filter.js"),require("core-js/modules/esnext.iterator.map.js"),require("core-js/modules/esnext.set.add-all.js"),require("core-js/modules/esnext.set.delete-all.js"),require("core-js/modules/esnext.set.difference.js"),require("core-js/modules/esnext.set.every.js"),require("core-js/modules/esnext.set.filter.js"),require("core-js/modules/esnext.set.find.js"),require("core-js/modules/esnext.set.intersection.js"),require("core-js/modules/esnext.set.is-disjoint-from.js"),require("core-js/modules/esnext.set.is-subset-of.js"),require("core-js/modules/esnext.set.is-superset-of.js"),require("core-js/modules/esnext.set.join.js"),require("core-js/modules/esnext.set.map.js"),require("core-js/modules/esnext.set.reduce.js"),require("core-js/modules/esnext.set.some.js"),require("core-js/modules/esnext.set.symmetric-difference.js"),require("core-js/modules/esnext.set.union.js"),Object.defineProperty(exports,"__esModule",{value:!0}),exports.prettyPrint=exports.serializeOuter=exports.serializeChildren=void 0;const NodeService_1=require("../services/NodeService"),VOID_ELEMENTS=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]),NEVER_SERIALIZE=new Set(["innerHTML","clientHeight","clientLeft","clientTop","clientWidth","scrollHeight","scrollLeft","scrollTop","scrollWidth","offsetHeight","offsetLeft","offsetParent","offsetTop","offsetWidth"]),ATTRIBUTE_NAME={className:"class"},BOOLEAN_ATTRIBUTES=new Set(["hidden"]),escapeText=e=>e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">"),escapeAttributeValue=e=>e.replace(/&/g,"&").replace(/"/g,"""),serializeAttributes=e=>{let r=(e.getAttributeNames?e.getAttributeNames():[]).filter(e=>!NEVER_SERIALIZE.has(e)).map(r=>{if(BOOLEAN_ATTRIBUTES.has(r))return e[r]?` ${r}`:"";const t=e.getAttribute(r);return""===t?"":` ${ATTRIBUTE_NAME[r]||r}="${escapeAttributeValue(t)}"`}).join("");const t=e.style;return t&&t.cssText&&(r+=` style="${escapeAttributeValue(t.cssText)}"`),r},serializeNode=e=>{if(e.nodeType===NodeService_1.NodeService.TEXT_NODE)return escapeText(e.nodeValue||"");if(e.nodeType===NodeService_1.NodeService.COMMENT_NODE)return`\x3c!--${e.nodeValue||""}--\x3e`;if(e.nodeType!==NodeService_1.NodeService.ELEMENT_NODE)return"";const r=e.tagName,t=serializeAttributes(e);return VOID_ELEMENTS.has(r)?`<${r}${t}>`:`<${r}${t}>${serializeChildren(e)}</${r}>`},serializeChildren=e=>Array.from(e.childNodes).map(serializeNode).join("");exports.serializeChildren=serializeChildren;const serializeOuter=e=>serializeNode(e);exports.serializeOuter=serializeOuter;const prettyPrintNode=(e,r,t)=>{const s=t.repeat(r);if(e.nodeType===NodeService_1.NodeService.TEXT_NODE){const r=escapeText((e.nodeValue||"").trim());return r?`${s}${r}`:""}if(e.nodeType===NodeService_1.NodeService.COMMENT_NODE)return`${s}\x3c!--${e.nodeValue||""}--\x3e`;if(e.nodeType!==NodeService_1.NodeService.ELEMENT_NODE)return"";const i=e.tagName,o=serializeAttributes(e);if(VOID_ELEMENTS.has(i))return`${s}<${i}${o}>`;const n=Array.from(e.childNodes).map(e=>prettyPrintNode(e,r+1,t)).filter(e=>""!==e);return 0===n.length?`${s}<${i}${o}></${i}>`:`${s}<${i}${o}>\n${n.join("\n")}\n${s}</${i}>`},prettyPrint=(e,r=" ")=>prettyPrintNode(e,0,r);exports.prettyPrint=prettyPrint;
|