pseudo-dom 0.16.0 → 0.17.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.
@@ -1,269 +1,353 @@
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
- exports.HTMLElementService = void 0
14
- const ElementService_1 = require('./ElementService')
15
- const createEvent_1 = __importDefault(require('../factories/createEvent'))
16
- const activeElement_1 = require('../functions/activeElement')
17
- const createStyleDeclaration_1 = __importDefault(require('../factories/createStyleDeclaration'))
18
- const createDataset_1 = __importDefault(require('../factories/createDataset'))
19
- const parseHTML_1 = __importDefault(require('../factories/parseHTML'))
20
- /**
21
- * Simulate the behaviour of the HTMLElement Class when there is no DOM available.
22
- * @author Joshua Heagle <joshuaheagle@gmail.com>
23
- * @class
24
- * @augments PseudoElement
25
- * @property {boolean} hidden - State of whether element is visible
26
- * @property {number} offsetHeight - The height of the element as offset by the parent element
27
- * @property {number} offsetLeft - The position of the left side of the element based on the parent element
28
- * @property {PseudoHTMLElement} offsetParent - A reference to the closest positioned parent element
29
- * @property {number} offsetTop - The position of the top side of the element based on the parent element
30
- * @property {number} offsetWidth - The width of the element as offset by the parent element
31
- * @property {CSSStyleDeclarationService} style - The element's inline styles, live and settable per property
32
- * @property {Object.<string, string>} dataset - The element's data-* attributes, live, under their camelCase names
33
- * @property {string} title - The title attribute which affects the text visible on hover
34
- */
35
- class HTMLElementService extends ElementService_1.ElementService {
36
- /**
37
- * Simulate the HTMLElement object when the Dom is not available
38
- * @param {Object} [elementOptions={}]
39
- * @param {string} [elementOptions.tagName='']
40
- * @param {PseudoNode|Object} [elementOptions.parent={}]
41
- * @param {Array} [elementOptions.children=[]]
42
- * @constructor
43
- */
44
- constructor ({
45
- tagName = '',
46
- parent = null,
47
- children = []
48
- } = {}) {
49
- super({
50
- tagName,
51
- attributes: [{
52
- name: 'hidden',
53
- value: false
54
- }, {
55
- name: 'offsetHeight',
56
- value: 0
57
- }, {
58
- name: 'offsetLeft',
59
- value: 0
60
- }, {
61
- name: 'offsetParent',
62
- value: null
63
- }, {
64
- name: 'offsetTop',
65
- value: 0
66
- }, {
67
- name: 'offsetWidth',
68
- value: 0
69
- }, {
70
- name: 'title',
71
- value: ''
72
- }],
73
- parent,
74
- children
75
- })
76
- this.styleDeclaration = (0, createStyleDeclaration_1.default)()
77
- this.datasetProxy = (0, createDataset_1.default)(this)
78
- }
79
-
80
- /**
81
- * The element's inline styles: a live CSSStyleDeclaration-like object, so both style.setProperty('color', 'red')
82
- * and style.color = 'red' work.
83
- * @returns {CSSStyleDeclarationService}
84
- */
85
- get style () {
86
- return this.styleDeclaration
87
- }
88
-
89
- /**
90
- * The element's data-* attributes, live, under their camelCase names (data-foo-bar <-> dataset.fooBar). Backed
91
- * directly by getAttribute / setAttribute, so it is never out of sync with the attributes themselves.
92
- * @returns {Object.<string, string>}
93
- */
94
- get dataset () {
95
- return this.datasetProxy
96
- }
97
-
98
- /**
99
- * Style is not attribute-backed like most properties (see the constructor), so cloneNode needs its own copy of it.
100
- * @returns {NodeService}
101
- */
102
- cloneShallow () {
103
- const copy = super.cloneShallow()
104
- copy.style.cssText = this.style.cssText
105
- return copy
106
- }
107
-
108
- /**
109
- * Style is not attribute-backed like most properties, so isEqualNode needs to compare it separately too.
110
- * @param {NodeService} other The node to compare with
111
- * @returns {boolean}
112
- */
113
- equalsShallow (other) {
114
- return super.equalsShallow(other) && this.style.cssText === other.style.cssText
115
- }
116
-
117
- /**
118
- * Parses html with this class building each new element (matches HTML: parsed elements behave like plain
119
- * HTMLElements, not whatever specialized class happens to be setting innerHTML / outerHTML).
120
- * @param {string} html
121
- * @returns {Array<*>}
122
- */
123
- parse (html) {
124
- return (0, parseHTML_1.default)(html, this.ownerDocument, HTMLElementService)
125
- }
126
-
127
- /**
128
- * Replace this element's children by parsing html. innerHTML's setter is here rather than on ElementService (which
129
- * only has the getter) because building the new elements needs a concrete element class - see parse().
130
- * @param {string} html
131
- * @returns {undefined}
132
- */
133
- set innerHTML (html) {
134
- this.replaceChildren(...this.parse(html))
135
- }
136
-
137
- get innerHTML () {
138
- return super.innerHTML
139
- }
140
-
141
- /**
142
- * Replace this element itself, in its parent, by parsing html. Does nothing when it has no parent, like
143
- * replaceWith. outerHTML's setter is here rather than on ElementService for the same reason as innerHTML's.
144
- * @param {string} html
145
- * @returns {undefined}
146
- */
147
- set outerHTML (html) {
148
- this.replaceWith(...this.parse(html))
149
- }
150
-
151
- get outerHTML () {
152
- return super.outerHTML
153
- }
154
-
155
- /**
156
- * Parse html and insert the resulting nodes at the given position, like insertAdjacentElement /
157
- * insertAdjacentText.
158
- * @param {string} position beforebegin, afterbegin, beforeend or afterend
159
- * @param {string} html The markup to parse
160
- * @returns {undefined}
161
- * @throws {Error} When the position is not one of the four above
162
- */
163
- insertAdjacentHTML (position, html) {
164
- const nodes = this.parse(html)
165
- switch (position) {
166
- case 'beforebegin':
167
- this.before(...nodes)
168
- return
169
- case 'afterbegin':
170
- this.prepend(...nodes)
171
- return
172
- case 'beforeend':
173
- this.append(...nodes)
174
- return
175
- case 'afterend':
176
- this.after(...nodes)
177
- return
178
- default:
179
- throw new Error(`insertAdjacentHTML: "${position}" is not one of beforebegin, afterbegin, beforeend, afterend.`)
180
- }
181
- }
182
-
183
- /**
184
- * Whether this element can have the focus: form controls and links which are not disabled, and anything with a tabindex.
185
- * @returns {boolean}
186
- */
187
- get canFocus () {
188
- if (this.hasAttribute('disabled')) {
189
- return false
190
- }
191
- switch (this.tagName) {
192
- case 'button':
193
- case 'select':
194
- case 'textarea':
195
- return true
196
- case 'input':
197
- return String(this.getAttribute('type') || '').toLowerCase() !== 'hidden'
198
- case 'a':
199
- return this.hasAttribute('href') || this.hasAttribute('tabindex')
200
- default:
201
- return this.hasAttribute('tabindex')
202
- }
203
- }
204
-
205
- /**
206
- * Click the element: a click event is sent to it, which bubbles and can be cancelled, like one from a user but a
207
- * script made it (so it is not trusted). A disabled element does nothing.
208
- */
209
- click () {
210
- if (this.hasAttribute('disabled')) {
211
- return
212
- }
213
- this.dispatchEvent((0, createEvent_1.default)('click', {}, {
214
- browser: true
215
- }))
216
- }
217
-
218
- /**
219
- * Give the element the focus. The element which had it gets blur then focusout, and this one gets focus then
220
- * focusin (blur and focus do not bubble, focusin and focusout do). Nothing happens when the element cannot have the
221
- * focus or already has it.
222
- */
223
- focus () {
224
- const root = this.getRootNode()
225
- const previous = (0, activeElement_1.getActiveElement)(root)
226
- if (!this.canFocus || previous === this) {
227
- return
228
- }
229
- const send = (target, type, relatedTarget) => {
230
- target.dispatchEvent((0, createEvent_1.default)(type, {
231
- relatedTarget
232
- }, {
233
- browser: true,
234
- trusted: true
235
- }))
236
- }
237
- if (previous) {
238
- send(previous, 'blur', this)
239
- send(previous, 'focusout', this)
240
- }
241
- (0, activeElement_1.setActiveElement)(root, this)
242
- send(this, 'focus', previous)
243
- send(this, 'focusin', previous)
244
- }
245
-
246
- /**
247
- * Take the focus away from the element, when it has it: it gets blur then focusout.
248
- */
249
- blur () {
250
- const root = this.getRootNode()
251
- if ((0, activeElement_1.getActiveElement)(root) !== this) {
252
- return
253
- }
254
- (0, activeElement_1.setActiveElement)(root, null)
255
- this.dispatchEvent((0, createEvent_1.default)('blur', {
256
- relatedTarget: null
257
- }, {
258
- browser: true,
259
- trusted: true
260
- }))
261
- this.dispatchEvent((0, createEvent_1.default)('focusout', {
262
- relatedTarget: null
263
- }, {
264
- browser: true,
265
- trusted: true
266
- }))
267
- }
268
- }
269
- exports.HTMLElementService = HTMLElementService
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
+ exports.HTMLElementService = void 0
14
+ const ElementService_1 = require('./ElementService')
15
+ const createEvent_1 = __importDefault(require('../factories/createEvent'))
16
+ const activeElement_1 = require('../functions/activeElement')
17
+ const createStyleDeclaration_1 = __importDefault(require('../factories/createStyleDeclaration'))
18
+ const createDataset_1 = __importDefault(require('../factories/createDataset'))
19
+ const parseHTML_1 = __importDefault(require('../factories/parseHTML'))
20
+ /**
21
+ * Simulate the behaviour of the HTMLElement Class when there is no DOM available.
22
+ * @author Joshua Heagle <joshuaheagle@gmail.com>
23
+ * @class
24
+ * @augments PseudoElement
25
+ * @property {boolean} hidden - State of whether element is visible
26
+ * @property {number} offsetHeight - The height of the element as offset by the parent element
27
+ * @property {number} offsetLeft - The position of the left side of the element based on the parent element
28
+ * @property {PseudoHTMLElement} offsetParent - A reference to the closest positioned parent element
29
+ * @property {number} offsetTop - The position of the top side of the element based on the parent element
30
+ * @property {number} offsetWidth - The width of the element as offset by the parent element
31
+ * @property {CSSStyleDeclarationService} style - The element's inline styles, live and settable per property
32
+ * @property {Object.<string, string>} dataset - The element's data-* attributes, live, under their camelCase names
33
+ * @property {string} title - The title attribute which affects the text visible on hover
34
+ */
35
+ class HTMLElementService extends ElementService_1.ElementService {
36
+ /**
37
+ * Simulate the HTMLElement object when the Dom is not available
38
+ * @param {Object} [elementOptions={}]
39
+ * @param {string} [elementOptions.tagName='']
40
+ * @param {PseudoNode|Object} [elementOptions.parent={}]
41
+ * @param {Array} [elementOptions.children=[]]
42
+ * @constructor
43
+ */
44
+ constructor ({
45
+ tagName = '',
46
+ parent = null,
47
+ children = []
48
+ } = {}) {
49
+ super({
50
+ tagName,
51
+ attributes: [{
52
+ name: 'hidden',
53
+ value: false
54
+ }, {
55
+ name: 'offsetHeight',
56
+ value: 0
57
+ }, {
58
+ name: 'offsetLeft',
59
+ value: 0
60
+ }, {
61
+ name: 'offsetParent',
62
+ value: null
63
+ }, {
64
+ name: 'offsetTop',
65
+ value: 0
66
+ }, {
67
+ name: 'offsetWidth',
68
+ value: 0
69
+ }, {
70
+ name: 'title',
71
+ value: ''
72
+ }],
73
+ parent,
74
+ children
75
+ })
76
+ this.dirtyValue = null
77
+ this.dirtyChecked = null
78
+ this.styleDeclaration = (0, createStyleDeclaration_1.default)()
79
+ this.datasetProxy = (0, createDataset_1.default)(this)
80
+ }
81
+
82
+ /**
83
+ * The element's inline styles: a live CSSStyleDeclaration-like object, so both style.setProperty('color', 'red')
84
+ * and style.color = 'red' work.
85
+ * @returns {CSSStyleDeclarationService}
86
+ */
87
+ get style () {
88
+ return this.styleDeclaration
89
+ }
90
+
91
+ /**
92
+ * The element's data-* attributes, live, under their camelCase names (data-foo-bar <-> dataset.fooBar). Backed
93
+ * directly by getAttribute / setAttribute, so it is never out of sync with the attributes themselves.
94
+ * @returns {Object.<string, string>}
95
+ */
96
+ get dataset () {
97
+ return this.datasetProxy
98
+ }
99
+
100
+ /**
101
+ * Whether this tag has a `value` (the form controls which do).
102
+ * @returns {boolean}
103
+ */
104
+ hasValueProperty () {
105
+ return ['input', 'textarea', 'select', 'button', 'option', 'output'].indexOf(this.tagName.toLowerCase()) >= 0
106
+ }
107
+
108
+ /**
109
+ * Whether this tag has a `checked` (only input does).
110
+ * @returns {boolean}
111
+ */
112
+ hasCheckedProperty () {
113
+ return this.tagName.toLowerCase() === 'input'
114
+ }
115
+
116
+ /**
117
+ * Put a plain own property on the element, as assigning a property this element does not have does in the DOM.
118
+ * @param {string} name
119
+ * @param {*} value
120
+ * @returns {undefined}
121
+ */
122
+ setExpando (name, value) {
123
+ Object.defineProperty(this, name, {
124
+ value,
125
+ writable: true,
126
+ configurable: true,
127
+ enumerable: true
128
+ })
129
+ }
130
+
131
+ /**
132
+ * The current value of a form control. Until it is set (or edited), it is the value attribute (the default value),
133
+ * or '' when there is none ('on' for a checkbox or radio); setting it never changes the attribute, like the DOM's.
134
+ * Only form controls (input, textarea, select, button, option, output) have one.
135
+ * @returns {string|undefined}
136
+ */
137
+ get value () {
138
+ if (!this.hasValueProperty()) {
139
+ return undefined
140
+ }
141
+ if (this.dirtyValue !== null) {
142
+ return this.dirtyValue
143
+ }
144
+ const attribute = this.getAttribute('value')
145
+ if (attribute !== null) {
146
+ return String(attribute)
147
+ }
148
+ return this.tagName.toLowerCase() === 'input' && /^(checkbox|radio)$/i.test(String(this.getAttribute('type'))) ? 'on' : ''
149
+ }
150
+
151
+ set value (value) {
152
+ if (this.hasValueProperty()) {
153
+ this.dirtyValue = String(value)
154
+ } else {
155
+ this.setExpando('value', value)
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Whether a checkbox or radio input is checked. Until it is set, it follows the checked attribute (which sets the
161
+ * default); setting it never changes the attribute, like the DOM's. Only input has one.
162
+ * @returns {boolean|undefined}
163
+ */
164
+ get checked () {
165
+ if (!this.hasCheckedProperty()) {
166
+ return undefined
167
+ }
168
+ return this.dirtyChecked !== null ? this.dirtyChecked : this.hasAttribute('checked')
169
+ }
170
+
171
+ set checked (checked) {
172
+ if (this.hasCheckedProperty()) {
173
+ this.dirtyChecked = Boolean(checked)
174
+ } else {
175
+ this.setExpando('checked', checked)
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Style is not attribute-backed like most properties (see the constructor), so cloneNode needs its own copy of it,
181
+ * and a form control keeps its current value and checkedness (as the DOM's cloneNode does).
182
+ * @returns {NodeService}
183
+ */
184
+ cloneShallow () {
185
+ const copy = super.cloneShallow()
186
+ copy.style.cssText = this.style.cssText
187
+ copy.dirtyValue = this.dirtyValue
188
+ copy.dirtyChecked = this.dirtyChecked
189
+ return copy
190
+ }
191
+
192
+ /**
193
+ * Style is not attribute-backed like most properties, so isEqualNode needs to compare it separately too.
194
+ * @param {NodeService} other The node to compare with
195
+ * @returns {boolean}
196
+ */
197
+ equalsShallow (other) {
198
+ return super.equalsShallow(other) && this.style.cssText === other.style.cssText
199
+ }
200
+
201
+ /**
202
+ * Parses html with this class building each new element (matches HTML: parsed elements behave like plain
203
+ * HTMLElements, not whatever specialized class happens to be setting innerHTML / outerHTML).
204
+ * @param {string} html
205
+ * @returns {Array<*>}
206
+ */
207
+ parse (html) {
208
+ return (0, parseHTML_1.default)(html, this.ownerDocument, HTMLElementService)
209
+ }
210
+
211
+ /**
212
+ * Replace this element's children by parsing html. innerHTML's setter is here rather than on ElementService (which
213
+ * only has the getter) because building the new elements needs a concrete element class - see parse().
214
+ * @param {string} html
215
+ * @returns {undefined}
216
+ */
217
+ set innerHTML (html) {
218
+ this.replaceChildren(...this.parse(html))
219
+ }
220
+
221
+ get innerHTML () {
222
+ return super.innerHTML
223
+ }
224
+
225
+ /**
226
+ * Replace this element itself, in its parent, by parsing html. Does nothing when it has no parent, like
227
+ * replaceWith. outerHTML's setter is here rather than on ElementService for the same reason as innerHTML's.
228
+ * @param {string} html
229
+ * @returns {undefined}
230
+ */
231
+ set outerHTML (html) {
232
+ this.replaceWith(...this.parse(html))
233
+ }
234
+
235
+ get outerHTML () {
236
+ return super.outerHTML
237
+ }
238
+
239
+ /**
240
+ * Parse html and insert the resulting nodes at the given position, like insertAdjacentElement /
241
+ * insertAdjacentText.
242
+ * @param {string} position beforebegin, afterbegin, beforeend or afterend
243
+ * @param {string} html The markup to parse
244
+ * @returns {undefined}
245
+ * @throws {Error} When the position is not one of the four above
246
+ */
247
+ insertAdjacentHTML (position, html) {
248
+ const nodes = this.parse(html)
249
+ switch (position) {
250
+ case 'beforebegin':
251
+ this.before(...nodes)
252
+ return
253
+ case 'afterbegin':
254
+ this.prepend(...nodes)
255
+ return
256
+ case 'beforeend':
257
+ this.append(...nodes)
258
+ return
259
+ case 'afterend':
260
+ this.after(...nodes)
261
+ return
262
+ default:
263
+ throw new Error(`insertAdjacentHTML: "${position}" is not one of beforebegin, afterbegin, beforeend, afterend.`)
264
+ }
265
+ }
266
+
267
+ /**
268
+ * Whether this element can have the focus: form controls and links which are not disabled, and anything with a tabindex.
269
+ * @returns {boolean}
270
+ */
271
+ get canFocus () {
272
+ if (this.hasAttribute('disabled')) {
273
+ return false
274
+ }
275
+ switch (this.tagName) {
276
+ case 'button':
277
+ case 'select':
278
+ case 'textarea':
279
+ return true
280
+ case 'input':
281
+ return String(this.getAttribute('type') || '').toLowerCase() !== 'hidden'
282
+ case 'a':
283
+ return this.hasAttribute('href') || this.hasAttribute('tabindex')
284
+ default:
285
+ return this.hasAttribute('tabindex')
286
+ }
287
+ }
288
+
289
+ /**
290
+ * Click the element: a click event is sent to it, which bubbles and can be cancelled, like one from a user but a
291
+ * script made it (so it is not trusted). A disabled element does nothing.
292
+ */
293
+ click () {
294
+ if (this.hasAttribute('disabled')) {
295
+ return
296
+ }
297
+ this.dispatchEvent((0, createEvent_1.default)('click', {}, {
298
+ browser: true
299
+ }))
300
+ }
301
+
302
+ /**
303
+ * Give the element the focus. The element which had it gets blur then focusout, and this one gets focus then
304
+ * focusin (blur and focus do not bubble, focusin and focusout do). Nothing happens when the element cannot have the
305
+ * focus or already has it.
306
+ */
307
+ focus () {
308
+ const root = this.getRootNode()
309
+ const previous = (0, activeElement_1.getActiveElement)(root)
310
+ if (!this.canFocus || previous === this) {
311
+ return
312
+ }
313
+ const send = (target, type, relatedTarget) => {
314
+ target.dispatchEvent((0, createEvent_1.default)(type, {
315
+ relatedTarget
316
+ }, {
317
+ browser: true,
318
+ trusted: true
319
+ }))
320
+ }
321
+ if (previous) {
322
+ send(previous, 'blur', this)
323
+ send(previous, 'focusout', this)
324
+ }
325
+ (0, activeElement_1.setActiveElement)(root, this)
326
+ send(this, 'focus', previous)
327
+ send(this, 'focusin', previous)
328
+ }
329
+
330
+ /**
331
+ * Take the focus away from the element, when it has it: it gets blur then focusout.
332
+ */
333
+ blur () {
334
+ const root = this.getRootNode()
335
+ if ((0, activeElement_1.getActiveElement)(root) !== this) {
336
+ return
337
+ }
338
+ (0, activeElement_1.setActiveElement)(root, null)
339
+ this.dispatchEvent((0, createEvent_1.default)('blur', {
340
+ relatedTarget: null
341
+ }, {
342
+ browser: true,
343
+ trusted: true
344
+ }))
345
+ this.dispatchEvent((0, createEvent_1.default)('focusout', {
346
+ relatedTarget: null
347
+ }, {
348
+ browser: true,
349
+ trusted: true
350
+ }))
351
+ }
352
+ }
353
+ exports.HTMLElementService = HTMLElementService
@@ -1 +1 @@
1
- "use strict";var __importDefault=function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(exports,"__esModule",{value:!0}),exports.HTMLElementService=void 0;const ElementService_1=require("./ElementService"),createEvent_1=__importDefault(require("../factories/createEvent")),activeElement_1=require("../functions/activeElement"),createStyleDeclaration_1=__importDefault(require("../factories/createStyleDeclaration")),createDataset_1=__importDefault(require("../factories/createDataset")),parseHTML_1=__importDefault(require("../factories/parseHTML"));class HTMLElementService extends ElementService_1.ElementService{constructor({tagName:e="",parent:t=null,children:r=[]}={}){super({tagName:e,attributes:[{name:"hidden",value:!1},{name:"offsetHeight",value:0},{name:"offsetLeft",value:0},{name:"offsetParent",value:null},{name:"offsetTop",value:0},{name:"offsetWidth",value:0},{name:"title",value:""}],parent:t,children:r}),this.styleDeclaration=(0,createStyleDeclaration_1.default)(),this.datasetProxy=(0,createDataset_1.default)(this)}get style(){return this.styleDeclaration}get dataset(){return this.datasetProxy}cloneShallow(){const e=super.cloneShallow();return e.style.cssText=this.style.cssText,e}equalsShallow(e){return super.equalsShallow(e)&&this.style.cssText===e.style.cssText}parse(e){return(0,parseHTML_1.default)(e,this.ownerDocument,HTMLElementService)}set innerHTML(e){this.replaceChildren(...this.parse(e))}get innerHTML(){return super.innerHTML}set outerHTML(e){this.replaceWith(...this.parse(e))}get outerHTML(){return super.outerHTML}insertAdjacentHTML(e,t){const r=this.parse(t);switch(e){case"beforebegin":return void this.before(...r);case"afterbegin":return void this.prepend(...r);case"beforeend":return void this.append(...r);case"afterend":return void this.after(...r);default:throw new Error(`insertAdjacentHTML: "${e}" is not one of beforebegin, afterbegin, beforeend, afterend.`)}}get canFocus(){if(this.hasAttribute("disabled"))return!1;switch(this.tagName){case"button":case"select":case"textarea":return!0;case"input":return"hidden"!==String(this.getAttribute("type")||"").toLowerCase();case"a":return this.hasAttribute("href")||this.hasAttribute("tabindex");default:return this.hasAttribute("tabindex")}}click(){this.hasAttribute("disabled")||this.dispatchEvent((0,createEvent_1.default)("click",{},{browser:!0}))}focus(){const e=this.getRootNode(),t=(0,activeElement_1.getActiveElement)(e);if(!this.canFocus||t===this)return;const r=(e,t,r)=>{e.dispatchEvent((0,createEvent_1.default)(t,{relatedTarget:r},{browser:!0,trusted:!0}))};t&&(r(t,"blur",this),r(t,"focusout",this)),(0,activeElement_1.setActiveElement)(e,this),r(this,"focus",t),r(this,"focusin",t)}blur(){const e=this.getRootNode();(0,activeElement_1.getActiveElement)(e)===this&&((0,activeElement_1.setActiveElement)(e,null),this.dispatchEvent((0,createEvent_1.default)("blur",{relatedTarget:null},{browser:!0,trusted:!0})),this.dispatchEvent((0,createEvent_1.default)("focusout",{relatedTarget:null},{browser:!0,trusted:!0})))}}exports.HTMLElementService=HTMLElementService;
1
+ "use strict";var __importDefault=function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(exports,"__esModule",{value:!0}),exports.HTMLElementService=void 0;const ElementService_1=require("./ElementService"),createEvent_1=__importDefault(require("../factories/createEvent")),activeElement_1=require("../functions/activeElement"),createStyleDeclaration_1=__importDefault(require("../factories/createStyleDeclaration")),createDataset_1=__importDefault(require("../factories/createDataset")),parseHTML_1=__importDefault(require("../factories/parseHTML"));class HTMLElementService extends ElementService_1.ElementService{constructor({tagName:e="",parent:t=null,children:r=[]}={}){super({tagName:e,attributes:[{name:"hidden",value:!1},{name:"offsetHeight",value:0},{name:"offsetLeft",value:0},{name:"offsetParent",value:null},{name:"offsetTop",value:0},{name:"offsetWidth",value:0},{name:"title",value:""}],parent:t,children:r}),this.dirtyValue=null,this.dirtyChecked=null,this.styleDeclaration=(0,createStyleDeclaration_1.default)(),this.datasetProxy=(0,createDataset_1.default)(this)}get style(){return this.styleDeclaration}get dataset(){return this.datasetProxy}hasValueProperty(){return["input","textarea","select","button","option","output"].indexOf(this.tagName.toLowerCase())>=0}hasCheckedProperty(){return"input"===this.tagName.toLowerCase()}setExpando(e,t){Object.defineProperty(this,e,{value:t,writable:!0,configurable:!0,enumerable:!0})}get value(){if(!this.hasValueProperty())return;if(null!==this.dirtyValue)return this.dirtyValue;const e=this.getAttribute("value");return null!==e?String(e):"input"===this.tagName.toLowerCase()&&/^(checkbox|radio)$/i.test(String(this.getAttribute("type")))?"on":""}set value(e){this.hasValueProperty()?this.dirtyValue=String(e):this.setExpando("value",e)}get checked(){if(this.hasCheckedProperty())return null!==this.dirtyChecked?this.dirtyChecked:this.hasAttribute("checked")}set checked(e){this.hasCheckedProperty()?this.dirtyChecked=Boolean(e):this.setExpando("checked",e)}cloneShallow(){const e=super.cloneShallow();return e.style.cssText=this.style.cssText,e.dirtyValue=this.dirtyValue,e.dirtyChecked=this.dirtyChecked,e}equalsShallow(e){return super.equalsShallow(e)&&this.style.cssText===e.style.cssText}parse(e){return(0,parseHTML_1.default)(e,this.ownerDocument,HTMLElementService)}set innerHTML(e){this.replaceChildren(...this.parse(e))}get innerHTML(){return super.innerHTML}set outerHTML(e){this.replaceWith(...this.parse(e))}get outerHTML(){return super.outerHTML}insertAdjacentHTML(e,t){const r=this.parse(t);switch(e){case"beforebegin":return void this.before(...r);case"afterbegin":return void this.prepend(...r);case"beforeend":return void this.append(...r);case"afterend":return void this.after(...r);default:throw new Error(`insertAdjacentHTML: "${e}" is not one of beforebegin, afterbegin, beforeend, afterend.`)}}get canFocus(){if(this.hasAttribute("disabled"))return!1;switch(this.tagName){case"button":case"select":case"textarea":return!0;case"input":return"hidden"!==String(this.getAttribute("type")||"").toLowerCase();case"a":return this.hasAttribute("href")||this.hasAttribute("tabindex");default:return this.hasAttribute("tabindex")}}click(){this.hasAttribute("disabled")||this.dispatchEvent((0,createEvent_1.default)("click",{},{browser:!0}))}focus(){const e=this.getRootNode(),t=(0,activeElement_1.getActiveElement)(e);if(!this.canFocus||t===this)return;const r=(e,t,r)=>{e.dispatchEvent((0,createEvent_1.default)(t,{relatedTarget:r},{browser:!0,trusted:!0}))};t&&(r(t,"blur",this),r(t,"focusout",this)),(0,activeElement_1.setActiveElement)(e,this),r(this,"focus",t),r(this,"focusin",t)}blur(){const e=this.getRootNode();(0,activeElement_1.getActiveElement)(e)===this&&((0,activeElement_1.setActiveElement)(e,null),this.dispatchEvent((0,createEvent_1.default)("blur",{relatedTarget:null},{browser:!0,trusted:!0})),this.dispatchEvent((0,createEvent_1.default)("focusout",{relatedTarget:null},{browser:!0,trusted:!0})))}}exports.HTMLElementService=HTMLElementService;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pseudo-dom",
3
3
  "description": "Mock the DOM on the server side for managing state and testing",
4
- "version": "0.16.0",
4
+ "version": "0.17.0",
5
5
  "main": "dist/main.js",
6
6
  "types": "dist/main.d.ts",
7
7
  "author": "Joshua Heagle",