UNPKG

3.98 kBJavaScriptView Raw
1const escape = require('html-escaper').escape;
2
3const Attr = require('./Attr');
4const Element = require('./Element');
5const DOMStringMap = require('./DOMStringMap');
6const CSSStyleDeclaration = require('./CSSStyleDeclaration');
7
8const {setPrototypeOf} = Object;
9
10// interface HTMLElement // https://html.spec.whatwg.org/multipage/dom.html#htmlelement
11class HTMLElement extends Element {
12 constructor(ownerDocument, name) {
13 super(ownerDocument, name);
14 this.dataset = new DOMStringMap(this);
15 this.style = new CSSStyleDeclaration();
16 const style = new Attr(this, 'style', this.style);
17 this.attributes.push(style);
18 this.attributes.style = style;
19 this.__isCE = -1;
20 }
21 get isCustomElement() {
22 if (this.__isCE < 0) {
23 this.__isCE = 0;
24 const is = this.getAttribute('is') || this.nodeName;
25 const ceName = -1 < is.indexOf('-');
26 if (ceName) {
27 const Class = this.ownerDocument.customElements.get(is);
28 if (Class) {
29 this.__isCE = 1;
30 setPrototypeOf(this, Class.prototype);
31 }
32 }
33 }
34 return this.__isCE === 1;
35 }
36}
37
38[
39 'click',
40 'focus',
41 'blur'
42].forEach(type => {
43 Object.defineProperty(HTMLElement.prototype, type, {
44 configurable: true,
45 value: function () {
46 const {ownerDocument} = this;
47 ownerDocument.activeElement = type === 'blur' ? null : this;
48 const event = ownerDocument.createEvent('Event');
49 event.initEvent(type, true, true);
50 this.dispatchEvent(event);
51 }
52 });
53});
54
55[
56 'title',
57 'lang',
58 'translate',
59 'dir',
60 'hidden',
61 'tabIndex',
62 'accessKey',
63 'draggable',
64 'spellcheck',
65 'contentEditable'
66].forEach(name => {
67 const lowName = name;
68 Object.defineProperty(HTMLElement.prototype, name, {
69 configurable: true,
70 get() { return this.getAttribute(lowName); },
71 set(value) { this.setAttribute(lowName, value); }
72 });
73});
74
75// HTMLElement implements GlobalEventHandlers;
76// HTMLElement implements DocumentAndElementEventHandlers;
77
78[
79 'onabort',
80 'onblur',
81 'oncancel',
82 'oncanplay',
83 'oncanplaythrough',
84 'onchange',
85 'onclick',
86 'onclose',
87 'oncontextmenu',
88 'oncuechange',
89 'ondblclick',
90 'ondrag',
91 'ondragend',
92 'ondragenter',
93 'ondragleave',
94 'ondragover',
95 'ondragstart',
96 'ondrop',
97 'ondurationchange',
98 'onemptied',
99 'onended',
100 'onerror',
101 'onfocus',
102 'oninput',
103 'oninvalid',
104 'onkeydown',
105 'onkeypress',
106 'onkeyup',
107 'onload',
108 'onloadeddata',
109 'onloadedmetadata',
110 'onloadstart',
111 'onmousedown',
112 'onmouseenter',
113 'onmouseleave',
114 'onmousemove',
115 'onmouseout',
116 'onmouseover',
117 'onmouseup',
118 'onmousewheel',
119 'onpause',
120 'onplay',
121 'onplaying',
122 'onprogress',
123 'onratechange',
124 'onreset',
125 'onresize',
126 'onscroll',
127 'onseeked',
128 'onseeking',
129 'onselect',
130 'onshow',
131 'onstalled',
132 'onsubmit',
133 'onsuspend',
134 'ontimeupdate',
135 'ontoggle',
136 'onvolumechange',
137 'onwaiting',
138 'onauxclick',
139 'ongotpointercapture',
140 'onlostpointercapture',
141 'onpointercancel',
142 'onpointerdown',
143 'onpointerenter',
144 'onpointerleave',
145 'onpointermove',
146 'onpointerout',
147 'onpointerover',
148 'onpointerup'
149].forEach(ontype => {
150 let _value = null;
151 const type = ontype.slice(2);
152 Object.defineProperty(HTMLElement.prototype, ontype, {
153 configurable: true,
154 get() {
155 return _value;
156 },
157 set(value) {
158 if (!value) {
159 if (_value) {
160 value = _value;
161 _value = null;
162 this.removeEventListener(type, value);
163 }
164 this.removeAttribute(ontype);
165 } else {
166 _value = value;
167 this.addEventListener(type, value);
168 this.setAttribute(ontype, 'return (' + escape(
169 JS_SHORTCUT.test(value) && !JS_FUNCTION.test(value) ?
170 ('function ' + value) :
171 ('' + value)
172 ) + ').call(this, event)');
173 }
174 }
175 });
176});
177
178// helpers
179const JS_SHORTCUT = /^[a-z$_]\S*?\(/;
180const JS_FUNCTION = /^function\S*?\(/;
181
182module.exports = HTMLElement;