pseudo-dom 0.9.0 → 0.10.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 +765 -2
- package/browser/pseudo-dom.js +755 -427
- package/browser/pseudo-dom.min.js +1 -1
- package/dist/factories/createDataset.d.ts +10 -10
- package/dist/factories/createDataset.js +78 -78
- package/dist/factories/createStyleDeclaration.d.ts +30 -30
- package/dist/factories/createStyleDeclaration.js +64 -64
- package/dist/interfaces/PseudoCSSStyleDeclaration.d.ts +14 -14
- package/dist/interfaces/PseudoCSSStyleDeclaration.js +5 -5
- package/dist/interfaces/PseudoElement.d.ts +765 -702
- package/dist/interfaces/PseudoHTMLElement.d.ts +33 -33
- package/dist/interfaces/PseudoShadowRoot.d.ts +12 -3
- package/dist/services/CSSStyleDeclarationService.d.ts +73 -73
- package/dist/services/CSSStyleDeclarationService.js +156 -156
- package/dist/services/ElementService.d.ts +361 -191
- package/dist/services/ElementService.js +717 -426
- package/dist/services/ElementService.min.js +1 -1
- package/dist/services/HTMLElementService.d.ts +87 -87
- package/dist/services/HTMLElementService.js +202 -202
- package/dist/services/NodeService.d.ts +305 -297
- package/dist/services/NodeService.js +732 -721
- package/dist/services/NodeService.min.js +1 -1
- package/dist/services/ShadowRootService.d.ts +16 -4
- package/dist/services/ShadowRootService.js +24 -9
- package/dist/services/ShadowRootService.min.js +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";require("core-js/modules/esnext.iterator.constructor.js"),require("core-js/modules/esnext.iterator.every.js"),require("core-js/modules/esnext.iterator.find.js"),require("core-js/modules/esnext.iterator.for-each.js"),require("core-js/modules/esnext.iterator.map.js"),require("core-js/modules/esnext.iterator.some.js");var __importDefault=function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(exports,"__esModule",{value:!0}),exports.ElementService=void 0;const createEvent_1=__importDefault(require("../factories/createEvent")),NodeService_1=require("./NodeService"),AttrService_1=require("./AttrService"),DOMTokenListService_1=require("./DOMTokenListService"),NamedNodeMapService_1=require("./NamedNodeMapService"),getParentNodesFromAttribute_1=__importDefault(require("../functions/getParentNodesFromAttribute")),HTMLCollectionService_1=require("./HTMLCollectionService"),cloneObject_1=__importDefault(require("si-funciona/dist/helpers/objects/cloneObject")),isEqual_1=__importDefault(require("si-funciona/dist/helpers/objects/isEqual")),query_1=require("../factories/query");class ElementService extends NodeService_1.NodeService{constructor({tagName:e="",attributes:t=[],parent:r=null,children:
|
|
1
|
+
"use strict";require("core-js/modules/esnext.iterator.constructor.js"),require("core-js/modules/esnext.iterator.every.js"),require("core-js/modules/esnext.iterator.find.js"),require("core-js/modules/esnext.iterator.for-each.js"),require("core-js/modules/esnext.iterator.map.js"),require("core-js/modules/esnext.iterator.some.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");var __importDefault=function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(exports,"__esModule",{value:!0}),exports.ElementService=void 0;const createEvent_1=__importDefault(require("../factories/createEvent")),NodeService_1=require("./NodeService"),AttrService_1=require("./AttrService"),DOMTokenListService_1=require("./DOMTokenListService"),NamedNodeMapService_1=require("./NamedNodeMapService"),getParentNodesFromAttribute_1=__importDefault(require("../functions/getParentNodesFromAttribute")),HTMLCollectionService_1=require("./HTMLCollectionService"),cloneObject_1=__importDefault(require("si-funciona/dist/helpers/objects/cloneObject")),isEqual_1=__importDefault(require("si-funciona/dist/helpers/objects/isEqual")),query_1=require("../factories/query"),ShadowRootService_1=require("./ShadowRootService"),zeroRect=()=>({x:0,y:0,width:0,height:0,top:0,right:0,bottom:0,left:0});class ElementService extends NodeService_1.NodeService{constructor({tagName:e="",attributes:t=[],parent:r=null,children:s=[]}={}){super(),this.boundingClientRect=zeroRect(),this.clientRects=[],this.animations=[],this.isVisible=!0,this.capturedPointers=new Set,this.shadowRootInstance=null,this.defaultEventApplied=!1,this.tokenList=new DOMTokenListService_1.DOMTokenListService,this.tag=e,this.attributeList=t.concat([{name:"className",value:""},{name:"id",value:""},{name:"innerHTML",value:""},{name:"clientHeight",value:0},{name:"clientLeft",value:0},{name:"clientTop",value:0},{name:"clientWidth",value:0},{name:"scrollHeight",value:0},{name:"scrollLeft",value:0},{name:"scrollTop",value:0},{name:"scrollWidth",value:0}]),this.propertyAttributes=this.attributeList.map(({name:e})=>e),this.attributeList.forEach(({name:e,value:t})=>{this[e]=t}),s.forEach(e=>{if(!e||"number"!=typeof e.nodeType)throw new TypeError("The children of an element must be nodes.");this.appendChild(e)}),r&&r.appendChild(this)}currentAttributes(){return this.attributeList.map(({name:e,value:t})=>({name:e,value:this.propertyAttributes.indexOf(e)>=0?this[e]:t}))}get nodeName(){return this.tag}cloneShallow(){const e=new this.constructor({tagName:this.tag});return e.attributeList.length=0,this.currentAttributes().forEach(({name:t,value:r})=>{const s="object"==typeof r&&null!==r?(0,cloneObject_1.default)(r):r;e.attributeList.push({name:t,value:s}),e.propertyAttributes.indexOf(t)>=0&&(e[t]=s)}),e.ownerDocumentStore=this.ownerDocumentStore,e}equalsShallow(e){const t=this.currentAttributes(),r=e.currentAttributes();return this.nodeName===e.nodeName&&t.length===r.length&&t.every(({name:e,value:t})=>{const s=r.find(t=>t.name===e);return void 0!==s&&(0,isEqual_1.default)(t,s.value)})}get tagName(){return this.tag}get localName(){return this.tag}get prefix(){return null}get nodeType(){return NodeService_1.NodeService.ELEMENT_NODE}get attributes(){return new NamedNodeMapService_1.NamedNodeMapService(this.currentAttributes().map(({name:e,value:t})=>new AttrService_1.AttrService(e,String(t),this)))}get classList(){return this.tokenList}get className(){return this.tokenList.value}set className(e){this.tokenList.value=e}get children(){return new HTMLCollectionService_1.HTMLCollectionService(this)}get childElementCount(){return this.children.length}get firstElementChild(){return this.children.item(0)}get lastElementChild(){const e=this.children;return e.item(e.length-1)}get nextElementSibling(){let e=this.nextSibling;for(;e&&e.nodeType!==NodeService_1.NodeService.ELEMENT_NODE;)e=e.nextSibling;return e}get previousElementSibling(){let e=this.previousSibling;for(;e&&e.nodeType!==NodeService_1.NodeService.ELEMENT_NODE;)e=e.previousSibling;return e}insertAdjacentElement(e,t){return this.insertAdjacent(e,t)}insertAdjacentText(e,t){this.insertAdjacent(e,t)}insertAdjacent(e,t){switch(e){case"beforebegin":return this.parentNode?(this.before(t),t):null;case"afterbegin":return this.prepend(t),t;case"beforeend":return this.append(t),t;case"afterend":return this.parentNode?(this.after(t),t):null;default:throw new Error(`"${e}" is not one of beforebegin, afterbegin, beforeend or afterend.`)}}insertAdjacentHTML(e,t){throw new Error("ElementService.insertAdjacentHTML() is not implemented yet.")}matches(e){return(0,query_1.matches)(this,e)}closest(e){return(0,query_1.closest)(this,e)}applyDefaultEvent(){let e=e=>{};if(this.defaultEventApplied)return e;switch(this.tagName){case"button":case"input":e=e=>{const t=String(this.getAttribute("type")||this.type||"").toLowerCase(),r="button"===this.tagName?"button"!==t&&"reset"!==t:/^(submit|image)$/.test(t),s=(0,getParentNodesFromAttribute_1.default)("tagName","form",this);r&&s.length&&!this.hasAttribute("disabled")&&s[s.length-1].dispatchEvent((0,createEvent_1.default)("submit",{},{browser:!0,trusted:e.isTrusted}))},super.setDefaultEvent("click",e),this.defaultEventApplied=!0}return e}childInserted(e){"function"==typeof e.applyDefaultEvent&&e.applyDefaultEvent()}hasAttribute(e){return this.attributeList.some(({name:t})=>t===e)}setAttribute(e,t){const r=this.attributeList.find(({name:t})=>t===e);r?r.value=t:this.attributeList.push({name:e,value:t}),this.propertyAttributes.indexOf(e)>=0&&(this[e]=t)}getAttribute(e){const t=this.currentAttributes().find(({name:t})=>t===e);return t?t.value:null}removeAttribute(e){const t=this.attributeList.findIndex(({name:t})=>t===e);t>=0&&this.attributeList.splice(t,1)}getAttributeNames(){return this.currentAttributes().map(({name:e})=>e)}hasAttributes(){return this.attributeList.length>0}toggleAttribute(e,t){const r=this.hasAttribute(e),s="boolean"==typeof t?t:!r;return s&&!r?this.setAttribute(e,""):!s&&r&&this.removeAttribute(e),s}getBoundingClientRect(){return this.boundingClientRect}getClientRects(){return this.clientRects}getAnimations(){return this.animations}checkVisibility(){return this.isVisible}computedStyleMap(){const e=this.style;return{get:t=>{const r=e?e.getPropertyValue(t):"";return""===r?void 0:r}}}hasPointerCapture(e){return this.capturedPointers.has(e)}setPointerCapture(e){this.capturedPointers.add(e)}releasePointerCapture(e){this.capturedPointers.delete(e)}scroll(e=0,t=0){if("number"==typeof e)return this.scrollLeft=e,void(this.scrollTop=t);"number"==typeof e.left&&(this.scrollLeft=e.left),"number"==typeof e.top&&(this.scrollTop=e.top)}scrollTo(e=0,t=0){this.scroll(e,t)}scrollBy(e=0,t=0){"number"!=typeof e?this.scroll({left:this.scrollLeft+(e.left||0),top:this.scrollTop+(e.top||0)}):this.scroll(this.scrollLeft+e,this.scrollTop+t)}scrollIntoView(){}requestFullscreen(){return Promise.resolve()}requestPointerLock(){return Promise.resolve()}attachShadow(e){if(this.shadowRootInstance)throw new Error("Shadow root cannot be created on a host which already hosts a shadow tree.");const t=new ShadowRootService_1.ShadowRootService;return t.host=this,t.mode=e.mode,this.shadowRootInstance=t,t}get shadowRoot(){return this.shadowRootInstance&&"open"===this.shadowRootInstance.mode?this.shadowRootInstance:null}}exports.ElementService=ElementService;
|
|
@@ -1,87 +1,87 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file Substitute for the DOM HTMLElement Class.
|
|
3
|
-
* @author Joshua Heagle <joshuaheagle@gmail.com>
|
|
4
|
-
* @version 1.0.0
|
|
5
|
-
*/
|
|
6
|
-
import { PseudoHTMLElement } from '../interfaces/PseudoHTMLElement';
|
|
7
|
-
import { PseudoNode } from '../interfaces/PseudoNode';
|
|
8
|
-
import { NodeService } from './NodeService';
|
|
9
|
-
import { ElementService } from './ElementService';
|
|
10
|
-
import { CSSStyleDeclarationService } from './CSSStyleDeclarationService';
|
|
11
|
-
/**
|
|
12
|
-
* Simulate the behaviour of the HTMLElement Class when there is no DOM available.
|
|
13
|
-
* @author Joshua Heagle <joshuaheagle@gmail.com>
|
|
14
|
-
* @class
|
|
15
|
-
* @augments PseudoElement
|
|
16
|
-
* @property {boolean} hidden - State of whether element is visible
|
|
17
|
-
* @property {number} offsetHeight - The height of the element as offset by the parent element
|
|
18
|
-
* @property {number} offsetLeft - The position of the left side of the element based on the parent element
|
|
19
|
-
* @property {PseudoHTMLElement} offsetParent - A reference to the closest positioned parent element
|
|
20
|
-
* @property {number} offsetTop - The position of the top side of the element based on the parent element
|
|
21
|
-
* @property {number} offsetWidth - The width of the element as offset by the parent element
|
|
22
|
-
* @property {CSSStyleDeclarationService} style - The element's inline styles, live and settable per property
|
|
23
|
-
* @property {Object.<string, string>} dataset - The element's data-* attributes, live, under their camelCase names
|
|
24
|
-
* @property {string} title - The title attribute which affects the text visible on hover
|
|
25
|
-
*/
|
|
26
|
-
export declare class HTMLElementService extends ElementService implements Partial<PseudoHTMLElement> {
|
|
27
|
-
private readonly styleDeclaration;
|
|
28
|
-
private readonly datasetProxy;
|
|
29
|
-
/**
|
|
30
|
-
* Simulate the HTMLElement object when the Dom is not available
|
|
31
|
-
* @param {Object} [elementOptions={}]
|
|
32
|
-
* @param {string} [elementOptions.tagName='']
|
|
33
|
-
* @param {PseudoNode|Object} [elementOptions.parent={}]
|
|
34
|
-
* @param {Array} [elementOptions.children=[]]
|
|
35
|
-
* @constructor
|
|
36
|
-
*/
|
|
37
|
-
constructor({ tagName, parent, children }?: {
|
|
38
|
-
tagName?: string;
|
|
39
|
-
parent?: PseudoNode | null;
|
|
40
|
-
children?: Array<any>;
|
|
41
|
-
});
|
|
42
|
-
/**
|
|
43
|
-
* The element's inline styles: a live CSSStyleDeclaration-like object, so both style.setProperty('color', 'red')
|
|
44
|
-
* and style.color = 'red' work.
|
|
45
|
-
* @returns {CSSStyleDeclarationService}
|
|
46
|
-
*/
|
|
47
|
-
get style(): CSSStyleDeclarationService;
|
|
48
|
-
/**
|
|
49
|
-
* The element's data-* attributes, live, under their camelCase names (data-foo-bar <-> dataset.fooBar). Backed
|
|
50
|
-
* directly by getAttribute / setAttribute, so it is never out of sync with the attributes themselves.
|
|
51
|
-
* @returns {Object.<string, string>}
|
|
52
|
-
*/
|
|
53
|
-
get dataset(): {
|
|
54
|
-
[key: string]: string;
|
|
55
|
-
};
|
|
56
|
-
/**
|
|
57
|
-
* Style is not attribute-backed like most properties (see the constructor), so cloneNode needs its own copy of it.
|
|
58
|
-
* @returns {NodeService}
|
|
59
|
-
*/
|
|
60
|
-
protected cloneShallow(): NodeService;
|
|
61
|
-
/**
|
|
62
|
-
* Style is not attribute-backed like most properties, so isEqualNode needs to compare it separately too.
|
|
63
|
-
* @param {NodeService} other The node to compare with
|
|
64
|
-
* @returns {boolean}
|
|
65
|
-
*/
|
|
66
|
-
protected equalsShallow(other: NodeService): boolean;
|
|
67
|
-
/**
|
|
68
|
-
* Whether this element can have the focus: form controls and links which are not disabled, and anything with a tabindex.
|
|
69
|
-
* @returns {boolean}
|
|
70
|
-
*/
|
|
71
|
-
get canFocus(): boolean;
|
|
72
|
-
/**
|
|
73
|
-
* Click the element: a click event is sent to it, which bubbles and can be cancelled, like one from a user but a
|
|
74
|
-
* script made it (so it is not trusted). A disabled element does nothing.
|
|
75
|
-
*/
|
|
76
|
-
click(): void;
|
|
77
|
-
/**
|
|
78
|
-
* Give the element the focus. The element which had it gets blur then focusout, and this one gets focus then
|
|
79
|
-
* focusin (blur and focus do not bubble, focusin and focusout do). Nothing happens when the element cannot have the
|
|
80
|
-
* focus or already has it.
|
|
81
|
-
*/
|
|
82
|
-
focus(): void;
|
|
83
|
-
/**
|
|
84
|
-
* Take the focus away from the element, when it has it: it gets blur then focusout.
|
|
85
|
-
*/
|
|
86
|
-
blur(): void;
|
|
87
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* @file Substitute for the DOM HTMLElement Class.
|
|
3
|
+
* @author Joshua Heagle <joshuaheagle@gmail.com>
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
import { PseudoHTMLElement } from '../interfaces/PseudoHTMLElement';
|
|
7
|
+
import { PseudoNode } from '../interfaces/PseudoNode';
|
|
8
|
+
import { NodeService } from './NodeService';
|
|
9
|
+
import { ElementService } from './ElementService';
|
|
10
|
+
import { CSSStyleDeclarationService } from './CSSStyleDeclarationService';
|
|
11
|
+
/**
|
|
12
|
+
* Simulate the behaviour of the HTMLElement Class when there is no DOM available.
|
|
13
|
+
* @author Joshua Heagle <joshuaheagle@gmail.com>
|
|
14
|
+
* @class
|
|
15
|
+
* @augments PseudoElement
|
|
16
|
+
* @property {boolean} hidden - State of whether element is visible
|
|
17
|
+
* @property {number} offsetHeight - The height of the element as offset by the parent element
|
|
18
|
+
* @property {number} offsetLeft - The position of the left side of the element based on the parent element
|
|
19
|
+
* @property {PseudoHTMLElement} offsetParent - A reference to the closest positioned parent element
|
|
20
|
+
* @property {number} offsetTop - The position of the top side of the element based on the parent element
|
|
21
|
+
* @property {number} offsetWidth - The width of the element as offset by the parent element
|
|
22
|
+
* @property {CSSStyleDeclarationService} style - The element's inline styles, live and settable per property
|
|
23
|
+
* @property {Object.<string, string>} dataset - The element's data-* attributes, live, under their camelCase names
|
|
24
|
+
* @property {string} title - The title attribute which affects the text visible on hover
|
|
25
|
+
*/
|
|
26
|
+
export declare class HTMLElementService extends ElementService implements Partial<PseudoHTMLElement> {
|
|
27
|
+
private readonly styleDeclaration;
|
|
28
|
+
private readonly datasetProxy;
|
|
29
|
+
/**
|
|
30
|
+
* Simulate the HTMLElement object when the Dom is not available
|
|
31
|
+
* @param {Object} [elementOptions={}]
|
|
32
|
+
* @param {string} [elementOptions.tagName='']
|
|
33
|
+
* @param {PseudoNode|Object} [elementOptions.parent={}]
|
|
34
|
+
* @param {Array} [elementOptions.children=[]]
|
|
35
|
+
* @constructor
|
|
36
|
+
*/
|
|
37
|
+
constructor({ tagName, parent, children }?: {
|
|
38
|
+
tagName?: string;
|
|
39
|
+
parent?: PseudoNode | null;
|
|
40
|
+
children?: Array<any>;
|
|
41
|
+
});
|
|
42
|
+
/**
|
|
43
|
+
* The element's inline styles: a live CSSStyleDeclaration-like object, so both style.setProperty('color', 'red')
|
|
44
|
+
* and style.color = 'red' work.
|
|
45
|
+
* @returns {CSSStyleDeclarationService}
|
|
46
|
+
*/
|
|
47
|
+
get style(): CSSStyleDeclarationService;
|
|
48
|
+
/**
|
|
49
|
+
* The element's data-* attributes, live, under their camelCase names (data-foo-bar <-> dataset.fooBar). Backed
|
|
50
|
+
* directly by getAttribute / setAttribute, so it is never out of sync with the attributes themselves.
|
|
51
|
+
* @returns {Object.<string, string>}
|
|
52
|
+
*/
|
|
53
|
+
get dataset(): {
|
|
54
|
+
[key: string]: string;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Style is not attribute-backed like most properties (see the constructor), so cloneNode needs its own copy of it.
|
|
58
|
+
* @returns {NodeService}
|
|
59
|
+
*/
|
|
60
|
+
protected cloneShallow(): NodeService;
|
|
61
|
+
/**
|
|
62
|
+
* Style is not attribute-backed like most properties, so isEqualNode needs to compare it separately too.
|
|
63
|
+
* @param {NodeService} other The node to compare with
|
|
64
|
+
* @returns {boolean}
|
|
65
|
+
*/
|
|
66
|
+
protected equalsShallow(other: NodeService): boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Whether this element can have the focus: form controls and links which are not disabled, and anything with a tabindex.
|
|
69
|
+
* @returns {boolean}
|
|
70
|
+
*/
|
|
71
|
+
get canFocus(): boolean;
|
|
72
|
+
/**
|
|
73
|
+
* Click the element: a click event is sent to it, which bubbles and can be cancelled, like one from a user but a
|
|
74
|
+
* script made it (so it is not trusted). A disabled element does nothing.
|
|
75
|
+
*/
|
|
76
|
+
click(): void;
|
|
77
|
+
/**
|
|
78
|
+
* Give the element the focus. The element which had it gets blur then focusout, and this one gets focus then
|
|
79
|
+
* focusin (blur and focus do not bubble, focusin and focusout do). Nothing happens when the element cannot have the
|
|
80
|
+
* focus or already has it.
|
|
81
|
+
*/
|
|
82
|
+
focus(): void;
|
|
83
|
+
/**
|
|
84
|
+
* Take the focus away from the element, when it has it: it gets blur then focusout.
|
|
85
|
+
*/
|
|
86
|
+
blur(): void;
|
|
87
|
+
}
|
|
@@ -1,202 +1,202 @@
|
|
|
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
|
-
/**
|
|
20
|
-
* Simulate the behaviour of the HTMLElement Class when there is no DOM available.
|
|
21
|
-
* @author Joshua Heagle <joshuaheagle@gmail.com>
|
|
22
|
-
* @class
|
|
23
|
-
* @augments PseudoElement
|
|
24
|
-
* @property {boolean} hidden - State of whether element is visible
|
|
25
|
-
* @property {number} offsetHeight - The height of the element as offset by the parent element
|
|
26
|
-
* @property {number} offsetLeft - The position of the left side of the element based on the parent element
|
|
27
|
-
* @property {PseudoHTMLElement} offsetParent - A reference to the closest positioned parent element
|
|
28
|
-
* @property {number} offsetTop - The position of the top side of the element based on the parent element
|
|
29
|
-
* @property {number} offsetWidth - The width of the element as offset by the parent element
|
|
30
|
-
* @property {CSSStyleDeclarationService} style - The element's inline styles, live and settable per property
|
|
31
|
-
* @property {Object.<string, string>} dataset - The element's data-* attributes, live, under their camelCase names
|
|
32
|
-
* @property {string} title - The title attribute which affects the text visible on hover
|
|
33
|
-
*/
|
|
34
|
-
class HTMLElementService extends ElementService_1.ElementService {
|
|
35
|
-
/**
|
|
36
|
-
* Simulate the HTMLElement object when the Dom is not available
|
|
37
|
-
* @param {Object} [elementOptions={}]
|
|
38
|
-
* @param {string} [elementOptions.tagName='']
|
|
39
|
-
* @param {PseudoNode|Object} [elementOptions.parent={}]
|
|
40
|
-
* @param {Array} [elementOptions.children=[]]
|
|
41
|
-
* @constructor
|
|
42
|
-
*/
|
|
43
|
-
constructor ({
|
|
44
|
-
tagName = '',
|
|
45
|
-
parent = null,
|
|
46
|
-
children = []
|
|
47
|
-
} = {}) {
|
|
48
|
-
super({
|
|
49
|
-
tagName,
|
|
50
|
-
attributes: [{
|
|
51
|
-
name: 'hidden',
|
|
52
|
-
value: false
|
|
53
|
-
}, {
|
|
54
|
-
name: 'offsetHeight',
|
|
55
|
-
value: 0
|
|
56
|
-
}, {
|
|
57
|
-
name: 'offsetLeft',
|
|
58
|
-
value: 0
|
|
59
|
-
}, {
|
|
60
|
-
name: 'offsetParent',
|
|
61
|
-
value: null
|
|
62
|
-
}, {
|
|
63
|
-
name: 'offsetTop',
|
|
64
|
-
value: 0
|
|
65
|
-
}, {
|
|
66
|
-
name: 'offsetWidth',
|
|
67
|
-
value: 0
|
|
68
|
-
}, {
|
|
69
|
-
name: 'title',
|
|
70
|
-
value: ''
|
|
71
|
-
}],
|
|
72
|
-
parent,
|
|
73
|
-
children
|
|
74
|
-
})
|
|
75
|
-
this.styleDeclaration = (0, createStyleDeclaration_1.default)()
|
|
76
|
-
this.datasetProxy = (0, createDataset_1.default)(this)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* The element's inline styles: a live CSSStyleDeclaration-like object, so both style.setProperty('color', 'red')
|
|
81
|
-
* and style.color = 'red' work.
|
|
82
|
-
* @returns {CSSStyleDeclarationService}
|
|
83
|
-
*/
|
|
84
|
-
get style () {
|
|
85
|
-
return this.styleDeclaration
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* The element's data-* attributes, live, under their camelCase names (data-foo-bar <-> dataset.fooBar). Backed
|
|
90
|
-
* directly by getAttribute / setAttribute, so it is never out of sync with the attributes themselves.
|
|
91
|
-
* @returns {Object.<string, string>}
|
|
92
|
-
*/
|
|
93
|
-
get dataset () {
|
|
94
|
-
return this.datasetProxy
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Style is not attribute-backed like most properties (see the constructor), so cloneNode needs its own copy of it.
|
|
99
|
-
* @returns {NodeService}
|
|
100
|
-
*/
|
|
101
|
-
cloneShallow () {
|
|
102
|
-
const copy = super.cloneShallow()
|
|
103
|
-
copy.style.cssText = this.style.cssText
|
|
104
|
-
return copy
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Style is not attribute-backed like most properties, so isEqualNode needs to compare it separately too.
|
|
109
|
-
* @param {NodeService} other The node to compare with
|
|
110
|
-
* @returns {boolean}
|
|
111
|
-
*/
|
|
112
|
-
equalsShallow (other) {
|
|
113
|
-
return super.equalsShallow(other) && this.style.cssText === other.style.cssText
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Whether this element can have the focus: form controls and links which are not disabled, and anything with a tabindex.
|
|
118
|
-
* @returns {boolean}
|
|
119
|
-
*/
|
|
120
|
-
get canFocus () {
|
|
121
|
-
if (this.hasAttribute('disabled')) {
|
|
122
|
-
return false
|
|
123
|
-
}
|
|
124
|
-
switch (this.tagName) {
|
|
125
|
-
case 'button':
|
|
126
|
-
case 'select':
|
|
127
|
-
case 'textarea':
|
|
128
|
-
return true
|
|
129
|
-
case 'input':
|
|
130
|
-
return String(this.getAttribute('type') || '').toLowerCase() !== 'hidden'
|
|
131
|
-
case 'a':
|
|
132
|
-
return this.hasAttribute('href') || this.hasAttribute('tabindex')
|
|
133
|
-
default:
|
|
134
|
-
return this.hasAttribute('tabindex')
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Click the element: a click event is sent to it, which bubbles and can be cancelled, like one from a user but a
|
|
140
|
-
* script made it (so it is not trusted). A disabled element does nothing.
|
|
141
|
-
*/
|
|
142
|
-
click () {
|
|
143
|
-
if (this.hasAttribute('disabled')) {
|
|
144
|
-
return
|
|
145
|
-
}
|
|
146
|
-
this.dispatchEvent((0, createEvent_1.default)('click', {}, {
|
|
147
|
-
browser: true
|
|
148
|
-
}))
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Give the element the focus. The element which had it gets blur then focusout, and this one gets focus then
|
|
153
|
-
* focusin (blur and focus do not bubble, focusin and focusout do). Nothing happens when the element cannot have the
|
|
154
|
-
* focus or already has it.
|
|
155
|
-
*/
|
|
156
|
-
focus () {
|
|
157
|
-
const root = this.getRootNode()
|
|
158
|
-
const previous = (0, activeElement_1.getActiveElement)(root)
|
|
159
|
-
if (!this.canFocus || previous === this) {
|
|
160
|
-
return
|
|
161
|
-
}
|
|
162
|
-
const send = (target, type, relatedTarget) => {
|
|
163
|
-
target.dispatchEvent((0, createEvent_1.default)(type, {
|
|
164
|
-
relatedTarget
|
|
165
|
-
}, {
|
|
166
|
-
browser: true,
|
|
167
|
-
trusted: true
|
|
168
|
-
}))
|
|
169
|
-
}
|
|
170
|
-
if (previous) {
|
|
171
|
-
send(previous, 'blur', this)
|
|
172
|
-
send(previous, 'focusout', this)
|
|
173
|
-
}
|
|
174
|
-
(0, activeElement_1.setActiveElement)(root, this)
|
|
175
|
-
send(this, 'focus', previous)
|
|
176
|
-
send(this, 'focusin', previous)
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Take the focus away from the element, when it has it: it gets blur then focusout.
|
|
181
|
-
*/
|
|
182
|
-
blur () {
|
|
183
|
-
const root = this.getRootNode()
|
|
184
|
-
if ((0, activeElement_1.getActiveElement)(root) !== this) {
|
|
185
|
-
return
|
|
186
|
-
}
|
|
187
|
-
(0, activeElement_1.setActiveElement)(root, null)
|
|
188
|
-
this.dispatchEvent((0, createEvent_1.default)('blur', {
|
|
189
|
-
relatedTarget: null
|
|
190
|
-
}, {
|
|
191
|
-
browser: true,
|
|
192
|
-
trusted: true
|
|
193
|
-
}))
|
|
194
|
-
this.dispatchEvent((0, createEvent_1.default)('focusout', {
|
|
195
|
-
relatedTarget: null
|
|
196
|
-
}, {
|
|
197
|
-
browser: true,
|
|
198
|
-
trusted: true
|
|
199
|
-
}))
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
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
|
+
/**
|
|
20
|
+
* Simulate the behaviour of the HTMLElement Class when there is no DOM available.
|
|
21
|
+
* @author Joshua Heagle <joshuaheagle@gmail.com>
|
|
22
|
+
* @class
|
|
23
|
+
* @augments PseudoElement
|
|
24
|
+
* @property {boolean} hidden - State of whether element is visible
|
|
25
|
+
* @property {number} offsetHeight - The height of the element as offset by the parent element
|
|
26
|
+
* @property {number} offsetLeft - The position of the left side of the element based on the parent element
|
|
27
|
+
* @property {PseudoHTMLElement} offsetParent - A reference to the closest positioned parent element
|
|
28
|
+
* @property {number} offsetTop - The position of the top side of the element based on the parent element
|
|
29
|
+
* @property {number} offsetWidth - The width of the element as offset by the parent element
|
|
30
|
+
* @property {CSSStyleDeclarationService} style - The element's inline styles, live and settable per property
|
|
31
|
+
* @property {Object.<string, string>} dataset - The element's data-* attributes, live, under their camelCase names
|
|
32
|
+
* @property {string} title - The title attribute which affects the text visible on hover
|
|
33
|
+
*/
|
|
34
|
+
class HTMLElementService extends ElementService_1.ElementService {
|
|
35
|
+
/**
|
|
36
|
+
* Simulate the HTMLElement object when the Dom is not available
|
|
37
|
+
* @param {Object} [elementOptions={}]
|
|
38
|
+
* @param {string} [elementOptions.tagName='']
|
|
39
|
+
* @param {PseudoNode|Object} [elementOptions.parent={}]
|
|
40
|
+
* @param {Array} [elementOptions.children=[]]
|
|
41
|
+
* @constructor
|
|
42
|
+
*/
|
|
43
|
+
constructor ({
|
|
44
|
+
tagName = '',
|
|
45
|
+
parent = null,
|
|
46
|
+
children = []
|
|
47
|
+
} = {}) {
|
|
48
|
+
super({
|
|
49
|
+
tagName,
|
|
50
|
+
attributes: [{
|
|
51
|
+
name: 'hidden',
|
|
52
|
+
value: false
|
|
53
|
+
}, {
|
|
54
|
+
name: 'offsetHeight',
|
|
55
|
+
value: 0
|
|
56
|
+
}, {
|
|
57
|
+
name: 'offsetLeft',
|
|
58
|
+
value: 0
|
|
59
|
+
}, {
|
|
60
|
+
name: 'offsetParent',
|
|
61
|
+
value: null
|
|
62
|
+
}, {
|
|
63
|
+
name: 'offsetTop',
|
|
64
|
+
value: 0
|
|
65
|
+
}, {
|
|
66
|
+
name: 'offsetWidth',
|
|
67
|
+
value: 0
|
|
68
|
+
}, {
|
|
69
|
+
name: 'title',
|
|
70
|
+
value: ''
|
|
71
|
+
}],
|
|
72
|
+
parent,
|
|
73
|
+
children
|
|
74
|
+
})
|
|
75
|
+
this.styleDeclaration = (0, createStyleDeclaration_1.default)()
|
|
76
|
+
this.datasetProxy = (0, createDataset_1.default)(this)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* The element's inline styles: a live CSSStyleDeclaration-like object, so both style.setProperty('color', 'red')
|
|
81
|
+
* and style.color = 'red' work.
|
|
82
|
+
* @returns {CSSStyleDeclarationService}
|
|
83
|
+
*/
|
|
84
|
+
get style () {
|
|
85
|
+
return this.styleDeclaration
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* The element's data-* attributes, live, under their camelCase names (data-foo-bar <-> dataset.fooBar). Backed
|
|
90
|
+
* directly by getAttribute / setAttribute, so it is never out of sync with the attributes themselves.
|
|
91
|
+
* @returns {Object.<string, string>}
|
|
92
|
+
*/
|
|
93
|
+
get dataset () {
|
|
94
|
+
return this.datasetProxy
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Style is not attribute-backed like most properties (see the constructor), so cloneNode needs its own copy of it.
|
|
99
|
+
* @returns {NodeService}
|
|
100
|
+
*/
|
|
101
|
+
cloneShallow () {
|
|
102
|
+
const copy = super.cloneShallow()
|
|
103
|
+
copy.style.cssText = this.style.cssText
|
|
104
|
+
return copy
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Style is not attribute-backed like most properties, so isEqualNode needs to compare it separately too.
|
|
109
|
+
* @param {NodeService} other The node to compare with
|
|
110
|
+
* @returns {boolean}
|
|
111
|
+
*/
|
|
112
|
+
equalsShallow (other) {
|
|
113
|
+
return super.equalsShallow(other) && this.style.cssText === other.style.cssText
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Whether this element can have the focus: form controls and links which are not disabled, and anything with a tabindex.
|
|
118
|
+
* @returns {boolean}
|
|
119
|
+
*/
|
|
120
|
+
get canFocus () {
|
|
121
|
+
if (this.hasAttribute('disabled')) {
|
|
122
|
+
return false
|
|
123
|
+
}
|
|
124
|
+
switch (this.tagName) {
|
|
125
|
+
case 'button':
|
|
126
|
+
case 'select':
|
|
127
|
+
case 'textarea':
|
|
128
|
+
return true
|
|
129
|
+
case 'input':
|
|
130
|
+
return String(this.getAttribute('type') || '').toLowerCase() !== 'hidden'
|
|
131
|
+
case 'a':
|
|
132
|
+
return this.hasAttribute('href') || this.hasAttribute('tabindex')
|
|
133
|
+
default:
|
|
134
|
+
return this.hasAttribute('tabindex')
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Click the element: a click event is sent to it, which bubbles and can be cancelled, like one from a user but a
|
|
140
|
+
* script made it (so it is not trusted). A disabled element does nothing.
|
|
141
|
+
*/
|
|
142
|
+
click () {
|
|
143
|
+
if (this.hasAttribute('disabled')) {
|
|
144
|
+
return
|
|
145
|
+
}
|
|
146
|
+
this.dispatchEvent((0, createEvent_1.default)('click', {}, {
|
|
147
|
+
browser: true
|
|
148
|
+
}))
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Give the element the focus. The element which had it gets blur then focusout, and this one gets focus then
|
|
153
|
+
* focusin (blur and focus do not bubble, focusin and focusout do). Nothing happens when the element cannot have the
|
|
154
|
+
* focus or already has it.
|
|
155
|
+
*/
|
|
156
|
+
focus () {
|
|
157
|
+
const root = this.getRootNode()
|
|
158
|
+
const previous = (0, activeElement_1.getActiveElement)(root)
|
|
159
|
+
if (!this.canFocus || previous === this) {
|
|
160
|
+
return
|
|
161
|
+
}
|
|
162
|
+
const send = (target, type, relatedTarget) => {
|
|
163
|
+
target.dispatchEvent((0, createEvent_1.default)(type, {
|
|
164
|
+
relatedTarget
|
|
165
|
+
}, {
|
|
166
|
+
browser: true,
|
|
167
|
+
trusted: true
|
|
168
|
+
}))
|
|
169
|
+
}
|
|
170
|
+
if (previous) {
|
|
171
|
+
send(previous, 'blur', this)
|
|
172
|
+
send(previous, 'focusout', this)
|
|
173
|
+
}
|
|
174
|
+
(0, activeElement_1.setActiveElement)(root, this)
|
|
175
|
+
send(this, 'focus', previous)
|
|
176
|
+
send(this, 'focusin', previous)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Take the focus away from the element, when it has it: it gets blur then focusout.
|
|
181
|
+
*/
|
|
182
|
+
blur () {
|
|
183
|
+
const root = this.getRootNode()
|
|
184
|
+
if ((0, activeElement_1.getActiveElement)(root) !== this) {
|
|
185
|
+
return
|
|
186
|
+
}
|
|
187
|
+
(0, activeElement_1.setActiveElement)(root, null)
|
|
188
|
+
this.dispatchEvent((0, createEvent_1.default)('blur', {
|
|
189
|
+
relatedTarget: null
|
|
190
|
+
}, {
|
|
191
|
+
browser: true,
|
|
192
|
+
trusted: true
|
|
193
|
+
}))
|
|
194
|
+
this.dispatchEvent((0, createEvent_1.default)('focusout', {
|
|
195
|
+
relatedTarget: null
|
|
196
|
+
}, {
|
|
197
|
+
browser: true,
|
|
198
|
+
trusted: true
|
|
199
|
+
}))
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
exports.HTMLElementService = HTMLElementService
|