Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
titaniumnetwork-dev
GitHub Repository: titaniumnetwork-dev/Ultraviolet
Path: blob/main/src/client/dom/element.js
305 views
1
import EventEmitter from "events";
2
import HookEvent from "../hook.js";
3
4
/**
5
* @typedef {import('../index').default} UVClient
6
*/
7
8
class ElementApi extends EventEmitter {
9
/**
10
*
11
* @param {UVClient} ctx
12
*/
13
constructor(ctx) {
14
super();
15
this.ctx = ctx;
16
this.window = ctx.window;
17
this.Audio = this.window.Audio;
18
this.Element = this.window.Element;
19
this.elemProto = this.Element ? this.Element.prototype : {};
20
this.innerHTML = ctx.nativeMethods.getOwnPropertyDescriptor(
21
this.elemProto,
22
"innerHTML"
23
);
24
this.outerHTML = ctx.nativeMethods.getOwnPropertyDescriptor(
25
this.elemProto,
26
"outerHTML"
27
);
28
this.setAttribute = this.elemProto.setAttribute;
29
this.getAttribute = this.elemProto.getAttribute;
30
this.removeAttribute = this.elemProto.removeAttribute;
31
this.hasAttribute = this.elemProto.hasAttribute;
32
this.querySelector = this.elemProto.querySelector;
33
this.querySelectorAll = this.elemProto.querySelectorAll;
34
this.insertAdjacentHTML = this.elemProto.insertAdjacentHTML;
35
this.insertAdjacentText = this.elemProto.insertAdjacentText;
36
}
37
overrideQuerySelector() {
38
this.ctx.override(this.elemProto, "querySelector", (target, that, args) => {
39
if (!args.length) return target.apply(that, args);
40
let [selectors] = args;
41
42
const event = new HookEvent({ selectors }, target, that);
43
this.emit("querySelector", event);
44
45
if (event.intercepted) return event.returnValue;
46
return event.target.call(event.that, event.data.selectors);
47
});
48
}
49
overrideAttribute() {
50
this.ctx.override(this.elemProto, "getAttribute", (target, that, args) => {
51
if (!args.length) return target.apply(that, args);
52
let [name] = args;
53
54
const event = new HookEvent({ name }, target, that);
55
this.emit("getAttribute", event);
56
57
if (event.intercepted) return event.returnValue;
58
return event.target.call(event.that, event.data.name);
59
});
60
this.ctx.override(this.elemProto, "setAttribute", (target, that, args) => {
61
if (2 > args.length) return target.apply(that, args);
62
let [name, value] = args;
63
64
const event = new HookEvent({ name, value }, target, that);
65
this.emit("setAttribute", event);
66
67
if (event.intercepted) return event.returnValue;
68
return event.target.call(event.that, event.data.name, event.data.value);
69
});
70
this.ctx.override(this.elemProto, "hasAttribute", (target, that, args) => {
71
if (!args.length) return target.apply(that, args);
72
let [name] = args;
73
74
const event = new HookEvent({ name }, target, that);
75
this.emit("hasAttribute", event);
76
77
if (event.intercepted) return event.returnValue;
78
return event.target.call(event.that, event.data.name);
79
});
80
this.ctx.override(
81
this.elemProto,
82
"removeAttribute",
83
(target, that, args) => {
84
if (!args.length) return target.apply(that, args);
85
let [name] = args;
86
87
const event = new HookEvent({ name }, target, that);
88
this.emit("removeAttribute", event);
89
90
if (event.intercepted) return event.returnValue;
91
return event.target.call(event.that, event.data.name);
92
}
93
);
94
}
95
overrideAudio() {
96
this.ctx.override(
97
this.window,
98
"Audio",
99
(target, that, args) => {
100
if (!args.length) return new target(...args);
101
let [url] = args;
102
103
const event = new HookEvent({ url }, target, that);
104
this.emit("audio", event);
105
106
if (event.intercepted) return event.returnValue;
107
return new event.target(event.data.url);
108
},
109
true
110
);
111
}
112
overrideHtml() {
113
this.hookProperty(this.Element, "innerHTML", {
114
get: (target, that) => {
115
const event = new HookEvent({ value: target.call(that) }, target, that);
116
this.emit("getInnerHTML", event);
117
118
if (event.intercepted) return event.returnValue;
119
return event.data.value;
120
},
121
set: (target, that, [val]) => {
122
const event = new HookEvent({ value: val }, target, that);
123
this.emit("setInnerHTML", event);
124
125
if (event.intercepted) return event.returnValue;
126
target.call(that, event.data.value);
127
},
128
});
129
this.hookProperty(this.Element, "outerHTML", {
130
get: (target, that) => {
131
const event = new HookEvent({ value: target.call(that) }, target, that);
132
this.emit("getOuterHTML", event);
133
134
if (event.intercepted) return event.returnValue;
135
return event.data.value;
136
},
137
set: (target, that, [val]) => {
138
const event = new HookEvent({ value: val }, target, that);
139
this.emit("setOuterHTML", event);
140
141
if (event.intercepted) return event.returnValue;
142
target.call(that, event.data.value);
143
},
144
});
145
}
146
overrideInsertAdjacentHTML() {
147
this.ctx.override(
148
this.elemProto,
149
"insertAdjacentHTML",
150
(target, that, args) => {
151
if (2 > args.length) return target.apply(that, args);
152
let [position, html] = args;
153
154
const event = new HookEvent({ position, html }, target, that);
155
this.emit("insertAdjacentHTML", event);
156
157
if (event.intercepted) return event.returnValue;
158
return event.target.call(
159
event.that,
160
event.data.position,
161
event.data.html
162
);
163
}
164
);
165
}
166
overrideInsertAdjacentText() {
167
this.ctx.override(
168
this.elemProto,
169
"insertAdjacentText",
170
(target, that, args) => {
171
if (2 > args.length) return target.apply(that, args);
172
let [position, text] = args;
173
174
const event = new HookEvent({ position, text }, target, that);
175
this.emit("insertAdjacentText", event);
176
177
if (event.intercepted) return event.returnValue;
178
return event.target.call(
179
event.that,
180
event.data.position,
181
event.data.text
182
);
183
}
184
);
185
}
186
hookProperty(element, prop, handler) {
187
// if (!element || !(prop in element)) return false;
188
if (!element) return false;
189
190
if (this.ctx.nativeMethods.isArray(element)) {
191
for (const elem of element) {
192
this.hookProperty(elem, prop, handler);
193
}
194
return true;
195
}
196
197
const proto = element.prototype;
198
199
this.ctx.overrideDescriptor(proto, prop, handler);
200
201
return true;
202
}
203
}
204
205
export default ElementApi;
206
207