Path: blob/main/src/client/dom/element.js
305 views
import EventEmitter from "events";1import HookEvent from "../hook.js";23/**4* @typedef {import('../index').default} UVClient5*/67class ElementApi extends EventEmitter {8/**9*10* @param {UVClient} ctx11*/12constructor(ctx) {13super();14this.ctx = ctx;15this.window = ctx.window;16this.Audio = this.window.Audio;17this.Element = this.window.Element;18this.elemProto = this.Element ? this.Element.prototype : {};19this.innerHTML = ctx.nativeMethods.getOwnPropertyDescriptor(20this.elemProto,21"innerHTML"22);23this.outerHTML = ctx.nativeMethods.getOwnPropertyDescriptor(24this.elemProto,25"outerHTML"26);27this.setAttribute = this.elemProto.setAttribute;28this.getAttribute = this.elemProto.getAttribute;29this.removeAttribute = this.elemProto.removeAttribute;30this.hasAttribute = this.elemProto.hasAttribute;31this.querySelector = this.elemProto.querySelector;32this.querySelectorAll = this.elemProto.querySelectorAll;33this.insertAdjacentHTML = this.elemProto.insertAdjacentHTML;34this.insertAdjacentText = this.elemProto.insertAdjacentText;35}36overrideQuerySelector() {37this.ctx.override(this.elemProto, "querySelector", (target, that, args) => {38if (!args.length) return target.apply(that, args);39let [selectors] = args;4041const event = new HookEvent({ selectors }, target, that);42this.emit("querySelector", event);4344if (event.intercepted) return event.returnValue;45return event.target.call(event.that, event.data.selectors);46});47}48overrideAttribute() {49this.ctx.override(this.elemProto, "getAttribute", (target, that, args) => {50if (!args.length) return target.apply(that, args);51let [name] = args;5253const event = new HookEvent({ name }, target, that);54this.emit("getAttribute", event);5556if (event.intercepted) return event.returnValue;57return event.target.call(event.that, event.data.name);58});59this.ctx.override(this.elemProto, "setAttribute", (target, that, args) => {60if (2 > args.length) return target.apply(that, args);61let [name, value] = args;6263const event = new HookEvent({ name, value }, target, that);64this.emit("setAttribute", event);6566if (event.intercepted) return event.returnValue;67return event.target.call(event.that, event.data.name, event.data.value);68});69this.ctx.override(this.elemProto, "hasAttribute", (target, that, args) => {70if (!args.length) return target.apply(that, args);71let [name] = args;7273const event = new HookEvent({ name }, target, that);74this.emit("hasAttribute", event);7576if (event.intercepted) return event.returnValue;77return event.target.call(event.that, event.data.name);78});79this.ctx.override(80this.elemProto,81"removeAttribute",82(target, that, args) => {83if (!args.length) return target.apply(that, args);84let [name] = args;8586const event = new HookEvent({ name }, target, that);87this.emit("removeAttribute", event);8889if (event.intercepted) return event.returnValue;90return event.target.call(event.that, event.data.name);91}92);93}94overrideAudio() {95this.ctx.override(96this.window,97"Audio",98(target, that, args) => {99if (!args.length) return new target(...args);100let [url] = args;101102const event = new HookEvent({ url }, target, that);103this.emit("audio", event);104105if (event.intercepted) return event.returnValue;106return new event.target(event.data.url);107},108true109);110}111overrideHtml() {112this.hookProperty(this.Element, "innerHTML", {113get: (target, that) => {114const event = new HookEvent({ value: target.call(that) }, target, that);115this.emit("getInnerHTML", event);116117if (event.intercepted) return event.returnValue;118return event.data.value;119},120set: (target, that, [val]) => {121const event = new HookEvent({ value: val }, target, that);122this.emit("setInnerHTML", event);123124if (event.intercepted) return event.returnValue;125target.call(that, event.data.value);126},127});128this.hookProperty(this.Element, "outerHTML", {129get: (target, that) => {130const event = new HookEvent({ value: target.call(that) }, target, that);131this.emit("getOuterHTML", event);132133if (event.intercepted) return event.returnValue;134return event.data.value;135},136set: (target, that, [val]) => {137const event = new HookEvent({ value: val }, target, that);138this.emit("setOuterHTML", event);139140if (event.intercepted) return event.returnValue;141target.call(that, event.data.value);142},143});144}145overrideInsertAdjacentHTML() {146this.ctx.override(147this.elemProto,148"insertAdjacentHTML",149(target, that, args) => {150if (2 > args.length) return target.apply(that, args);151let [position, html] = args;152153const event = new HookEvent({ position, html }, target, that);154this.emit("insertAdjacentHTML", event);155156if (event.intercepted) return event.returnValue;157return event.target.call(158event.that,159event.data.position,160event.data.html161);162}163);164}165overrideInsertAdjacentText() {166this.ctx.override(167this.elemProto,168"insertAdjacentText",169(target, that, args) => {170if (2 > args.length) return target.apply(that, args);171let [position, text] = args;172173const event = new HookEvent({ position, text }, target, that);174this.emit("insertAdjacentText", event);175176if (event.intercepted) return event.returnValue;177return event.target.call(178event.that,179event.data.position,180event.data.text181);182}183);184}185hookProperty(element, prop, handler) {186// if (!element || !(prop in element)) return false;187if (!element) return false;188189if (this.ctx.nativeMethods.isArray(element)) {190for (const elem of element) {191this.hookProperty(elem, prop, handler);192}193return true;194}195196const proto = element.prototype;197198this.ctx.overrideDescriptor(proto, prop, handler);199200return true;201}202}203204export default ElementApi;205206207