/* Crafter.CppDOM Copyright (C) 2025 Catcrafts Catcrafts.net This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ const decoder = new TextDecoder(); const encoder = new TextEncoder(); let memorycounter = 0; const jsmemory = new Map(); const eventHandlers = new Map(); function freeJs(ptr) { jsmemory.delete(ptr); } function writeStringToWasm(str) { const encoder = new TextEncoder(); const encoded = encoder.encode(str + '\0'); const len = encoded.length; const { WasmAlloc } = window.crafter_webbuild_wasi.instance.exports; const ptr = WasmAlloc(len); const memView = new Uint8Array(window.crafter_webbuild_wasi.instance.exports.memory.buffer, ptr, len); memView.set(encoded); return ptr; } function getElementById(id, idLenght) { const obj = document.getElementById(decoder.decode(new Int8Array(window.crafter_webbuild_wasi.instance.exports.memory.buffer, id, idLenght))); jsmemory.set(++memorycounter, obj); return memorycounter; } function setInnerHTML(ptr, html, htmlLenght) { jsmemory.get(ptr).innerHTML = decoder.decode(new Int8Array(window.crafter_webbuild_wasi.instance.exports.memory.buffer, html, htmlLenght)); } // Event handler registration functions function addClickListener(ptr, handlerID) { const element = jsmemory.get(ptr); // Create a handler that will trigger a notification to C++ const handler = function (event) { const { ExecuteClickHandler } = window.crafter_webbuild_wasi.instance.exports; ExecuteClickHandler(handlerID, event.clientX, event.clientY, event.screenX, event.screenY, event.button, event.buttons, event.altKey, event.ctrlKey, event.shiftKey, event.metaKey); }; eventHandlers.set(`${ptr}-${handlerID}`, handler); element.addEventListener("click", handler); } function removeClickListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = eventHandlers.get(`${ptr}-${handlerID}`); element.removeEventListener("click", handler); eventHandlers.delete(`${ptr}-${handlerID}`); } // Mouse Events function addMouseOverListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = function (event) { const { ExecuteMouseOverHandler } = window.crafter_webbuild_wasi.instance.exports; ExecuteMouseOverHandler(handlerID, event.clientX, event.clientY, event.screenX, event.screenY, event.button, event.buttons, event.altKey, event.ctrlKey, event.shiftKey, event.metaKey); }; eventHandlers.set(`${ptr}-${handlerID}-mouseover`, handler); element.addEventListener("mouseover", handler); } function removeMouseOverListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = eventHandlers.get(`${ptr}-${handlerID}-mouseover`); element.removeEventListener("mouseover", handler); eventHandlers.delete(`${ptr}-${handlerID}-mouseover`); } function addMouseOutListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = function (event) { const { ExecuteMouseOutHandler } = window.crafter_webbuild_wasi.instance.exports; ExecuteMouseOutHandler(handlerID, event.clientX, event.clientY, event.screenX, event.screenY, event.button, event.buttons, event.altKey, event.ctrlKey, event.shiftKey, event.metaKey); }; eventHandlers.set(`${ptr}-${handlerID}-mouseout`, handler); element.addEventListener("mouseout", handler); } function removeMouseOutListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = eventHandlers.get(`${ptr}-${handlerID}-mouseout`); element.removeEventListener("mouseout", handler); eventHandlers.delete(`${ptr}-${handlerID}-mouseout`); } function addMouseMoveListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = function (event) { const { ExecuteMouseMoveHandler } = window.crafter_webbuild_wasi.instance.exports; ExecuteMouseMoveHandler(handlerID, event.clientX, event.clientY, event.screenX, event.screenY, event.button, event.buttons, event.altKey, event.ctrlKey, event.shiftKey, event.metaKey); }; eventHandlers.set(`${ptr}-${handlerID}-mousemove`, handler); element.addEventListener("mousemove", handler); } function removeMouseMoveListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = eventHandlers.get(`${ptr}-${handlerID}-mousemove`); element.removeEventListener("mousemove", handler); eventHandlers.delete(`${ptr}-${handlerID}-mousemove`); } function addMouseDownListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = function (event) { const { ExecuteMouseDownHandler } = window.crafter_webbuild_wasi.instance.exports; ExecuteMouseDownHandler(handlerID, event.clientX, event.clientY, event.screenX, event.screenY, event.button, event.buttons, event.altKey, event.ctrlKey, event.shiftKey, event.metaKey); }; eventHandlers.set(`${ptr}-${handlerID}-mousedown`, handler); element.addEventListener("mousedown", handler); } function removeMouseDownListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = eventHandlers.get(`${ptr}-${handlerID}-mousedown`); element.removeEventListener("mousedown", handler); eventHandlers.delete(`${ptr}-${handlerID}-mousedown`); } function addMouseUpListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = function (event) { const { ExecuteMouseUpHandler } = window.crafter_webbuild_wasi.instance.exports; ExecuteMouseUpHandler(handlerID, event.clientX, event.clientY, event.screenX, event.screenY, event.button, event.buttons, event.altKey, event.ctrlKey, event.shiftKey, event.metaKey); }; eventHandlers.set(`${ptr}-${handlerID}-mouseup`, handler); element.addEventListener("mouseup", handler); } function removeMouseUpListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = eventHandlers.get(`${ptr}-${handlerID}-mouseup`); element.removeEventListener("mouseup", handler); eventHandlers.delete(`${ptr}-${handlerID}-mouseup`); } // Focus Events function addFocusListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = function (event) { const { ExecuteFocusHandler } = window.crafter_webbuild_wasi.instance.exports; if(event.relatedTarget) { const targetKey = ++memorycounter; const relatedTargetKey = ++memorycounter; jsmemory.set(targetKey, event.target); jsmemory.set(relatedTargetKey, event.relatedTarget); ExecuteFocusHandler(handlerID, targetKey, relatedTargetKey); } else { const targetKey = ++memorycounter; jsmemory.set(targetKey, event.target); ExecuteFocusHandler(handlerID, targetKey, 0); } }; eventHandlers.set(`${ptr}-${handlerID}-focus`, handler); element.addEventListener("focus", handler); } function removeFocusListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = eventHandlers.get(`${ptr}-${handlerID}-focus`); element.removeEventListener("focus", handler); eventHandlers.delete(`${ptr}-${handlerID}-focus`); } function addBlurListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = function (event) { const { ExecuteBlurHandler } = window.crafter_webbuild_wasi.instance.exports; if(event.relatedTarget) { const targetKey = ++memorycounter; const relatedTargetKey = ++memorycounter; jsmemory.set(targetKey, event.target); jsmemory.set(relatedTargetKey, event.relatedTarget); ExecuteBlurHandler(handlerID, targetKey, relatedTargetKey); } else { const targetKey = ++memorycounter; jsmemory.set(targetKey, event.target); ExecuteBlurHandler(handlerID, targetKey, 0); } }; eventHandlers.set(`${ptr}-${handlerID}-blur`, handler); element.addEventListener("blur", handler); } function removeBlurListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = eventHandlers.get(`${ptr}-${handlerID}-blur`); element.removeEventListener("blur", handler); eventHandlers.delete(`${ptr}-${handlerID}-blur`); } // Keyboard Events function addKeyDownListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = function (event) { const { ExecuteKeyDownHandler, WasmFree } = window.crafter_webbuild_wasi.instance.exports; const ptr = writeStringToWasm(event.key); ExecuteKeyDownHandler(handlerID, ptr, event.keyCode, event.altKey, event.ctrlKey, event.shiftKey, event.metaKey); WasmFree(ptr); }; eventHandlers.set(`${ptr}-${handlerID}-keydown`, handler); element.addEventListener("keydown", handler); } function removeKeyDownListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = eventHandlers.get(`${ptr}-${handlerID}-keydown`); element.removeEventListener("keydown", handler); eventHandlers.delete(`${ptr}-${handlerID}-keydown`); } function addKeyUpListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = function (event) { const { ExecuteKeyUpHandler, WasmFree } = window.crafter_webbuild_wasi.instance.exports; const ptr = writeStringToWasm(event.key); ExecuteKeyUpHandler(handlerID, ptr, event.keyCode, event.altKey, event.ctrlKey, event.shiftKey, event.metaKey); WasmFree(ptr); }; eventHandlers.set(`${ptr}-${handlerID}-keyup`, handler); element.addEventListener("keyup", handler); } function removeKeyUpListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = eventHandlers.get(`${ptr}-${handlerID}-keyup`); element.removeEventListener("keyup", handler); eventHandlers.delete(`${ptr}-${handlerID}-keyup`); } function addKeyPressListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = function (event) { const { ExecuteKeyPressHandler, WasmFree } = window.crafter_webbuild_wasi.instance.exports; const ptr = writeStringToWasm(event.key); ExecuteKeyPressHandler(handlerID, ptr, event.keyCode, event.altKey, event.ctrlKey, event.shiftKey, event.metaKey); WasmFree(ptr); }; eventHandlers.set(`${ptr}-${handlerID}-keypress`, handler); element.addEventListener("keypress", handler); } function removeKeyPressListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = eventHandlers.get(`${ptr}-${handlerID}-keypress`); element.removeEventListener("keypress", handler); eventHandlers.delete(`${ptr}-${handlerID}-keypress`); } function addChangeListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = function (event) { const { ExecuteChangeHandler, WasmFree } = window.crafter_webbuild_wasi.instance.exports; const ptr = writeStringToWasm(event.target.value); ExecuteChangeHandler(handlerID, ptr); WasmFree(ptr); }; eventHandlers.set(`${ptr}-${handlerID}-change`, handler); element.addEventListener("change", handler); } function removeChangeListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = eventHandlers.get(`${ptr}-${handlerID}-change`); element.removeEventListener("change", handler); eventHandlers.delete(`${ptr}-${handlerID}-change`); } function addSubmitListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = function (event) { const { ExecuteSubmitHandler } = window.crafter_webbuild_wasi.instance.exports; ExecuteSubmitHandler(handlerID); }; eventHandlers.set(`${ptr}-${handlerID}-submit`, handler); element.addEventListener("submit", handler); } function removeSubmitListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = eventHandlers.get(`${ptr}-${handlerID}-submit`); element.removeEventListener("submit", handler); eventHandlers.delete(`${ptr}-${handlerID}-submit`); } function addInputListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = function (event) { const { ExecuteInputHandler, WasmFree } = window.crafter_webbuild_wasi.instance.exports; const ptr = writeStringToWasm(event.data || event.target.value); ExecuteInputHandler(handlerID, ptr, event.inputType === 'insertCompositionText'); WasmFree(ptr); }; eventHandlers.set(`${ptr}-${handlerID}-input`, handler); element.addEventListener("input", handler); } function removeInputListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = eventHandlers.get(`${ptr}-${handlerID}-input`); element.removeEventListener("input", handler); eventHandlers.delete(`${ptr}-${handlerID}-input`); } function addResizeListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = function (event) { const { ExecuteResizeHandler } = window.crafter_webbuild_wasi.instance.exports; ExecuteResizeHandler(handlerID, window.innerWidth, window.innerHeight); }; eventHandlers.set(`${ptr}-${handlerID}-resize`, handler); element.addEventListener("resize", handler); } function removeResizeListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = eventHandlers.get(`${ptr}-${handlerID}-resize`); element.removeEventListener("resize", handler); eventHandlers.delete(`${ptr}-${handlerID}-resize`); } function addScrollListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = function (event) { const { ExecuteScrollHandler } = window.crafter_webbuild_wasi.instance.exports; ExecuteScrollHandler(handlerID, window.scrollX, window.scrollY); }; eventHandlers.set(`${ptr}-${handlerID}-scroll`, handler); element.addEventListener("scroll", handler); } function removeScrollListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = eventHandlers.get(`${ptr}-${handlerID}-scroll`); element.removeEventListener("scroll", handler); eventHandlers.delete(`${ptr}-${handlerID}-scroll`); } // Context Menu Events function addContextMenuListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = function (event) { const { ExecuteContextMenuHandler } = window.crafter_webbuild_wasi.instance.exports; ExecuteContextMenuHandler(handlerID, event.clientX, event.clientY, event.screenX, event.screenY, event.button, event.buttons, event.altKey, event.ctrlKey, event.shiftKey, event.metaKey); }; eventHandlers.set(`${ptr}-${handlerID}-contextmenu`, handler); element.addEventListener("contextmenu", handler); } function removeContextMenuListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = eventHandlers.get(`${ptr}-${handlerID}-contextmenu`); element.removeEventListener("contextmenu", handler); eventHandlers.delete(`${ptr}-${handlerID}-contextmenu`); } // Drag and Drop Events function addDragStartListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = function (event) { const { ExecuteDragStartHandler } = window.crafter_webbuild_wasi.instance.exports; ExecuteDragStartHandler(handlerID, event.clientX, event.clientY, event.screenX, event.screenY, event.button, event.buttons, event.altKey, event.ctrlKey, event.shiftKey, event.metaKey); }; eventHandlers.set(`${ptr}-${handlerID}-dragstart`, handler); element.addEventListener("dragstart", handler); } function removeDragStartListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = eventHandlers.get(`${ptr}-${handlerID}-dragstart`); element.removeEventListener("dragstart", handler); eventHandlers.delete(`${ptr}-${handlerID}-dragstart`); } function addDragEndListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = function (event) { const { ExecuteDragEndHandler } = window.crafter_webbuild_wasi.instance.exports; ExecuteDragEndHandler(handlerID, event.clientX, event.clientY, event.screenX, event.screenY, event.button, event.buttons, event.altKey, event.ctrlKey, event.shiftKey, event.metaKey); }; eventHandlers.set(`${ptr}-${handlerID}-dragend`, handler); element.addEventListener("dragend", handler); } function removeDragEndListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = eventHandlers.get(`${ptr}-${handlerID}-dragend`); element.removeEventListener("dragend", handler); eventHandlers.delete(`${ptr}-${handlerID}-dragend`); } function addDropListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = function (event) { const { ExecuteDropHandler } = window.crafter_webbuild_wasi.instance.exports; ExecuteDropHandler(handlerID, event.clientX, event.clientY, event.screenX, event.screenY, event.button, event.buttons, event.altKey, event.ctrlKey, event.shiftKey, event.metaKey); }; eventHandlers.set(`${ptr}-${handlerID}-drop`, handler); element.addEventListener("drop", handler); } function removeDropListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = eventHandlers.get(`${ptr}-${handlerID}-drop`); element.removeEventListener("drop", handler); eventHandlers.delete(`${ptr}-${handlerID}-drop`); } // Additional Drag Events function addDragOverListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = function (event) { const { ExecuteDragOverHandler } = window.crafter_webbuild_wasi.instance.exports; ExecuteDragOverHandler(handlerID, event.clientX, event.clientY, event.screenX, event.screenY, event.button, event.buttons, event.altKey, event.ctrlKey, event.shiftKey, event.metaKey); }; eventHandlers.set(`${ptr}-${handlerID}-dragover`, handler); element.addEventListener("dragover", handler); } function removeDragOverListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = eventHandlers.get(`${ptr}-${handlerID}-dragover`); element.removeEventListener("dragover", handler); eventHandlers.delete(`${ptr}-${handlerID}-dragover`); } function addDragEnterListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = function (event) { const { ExecuteDragEnterHandler } = window.crafter_webbuild_wasi.instance.exports; ExecuteDragEnterHandler(handlerID, event.clientX, event.clientY, event.screenX, event.screenY, event.button, event.buttons, event.altKey, event.ctrlKey, event.shiftKey, event.metaKey); }; eventHandlers.set(`${ptr}-${handlerID}-dragenter`, handler); element.addEventListener("dragenter", handler); } function removeDragEnterListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = eventHandlers.get(`${ptr}-${handlerID}-dragenter`); element.removeEventListener("dragenter", handler); eventHandlers.delete(`${ptr}-${handlerID}-dragenter`); } function addDragLeaveListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = function (event) { const { ExecuteDragLeaveHandler } = window.crafter_webbuild_wasi.instance.exports; ExecuteDragLeaveHandler(handlerID, event.clientX, event.clientY, event.screenX, event.screenY, event.button, event.buttons, event.altKey, event.ctrlKey, event.shiftKey, event.metaKey); }; eventHandlers.set(`${ptr}-${handlerID}-dragleave`, handler); element.addEventListener("dragleave", handler); } function removeDragLeaveListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = eventHandlers.get(`${ptr}-${handlerID}-dragleave`); element.removeEventListener("dragleave", handler); eventHandlers.delete(`${ptr}-${handlerID}-dragleave`); } // Wheel Event function addWheelListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = function (event) { const { ExecuteWheelHandler } = window.crafter_webbuild_wasi.instance.exports; ExecuteWheelHandler(handlerID, event.deltaX, event.deltaY, event.deltaZ, event.deltaMode, event.clientX, event.clientY, event.screenX, event.screenY, event.button, event.buttons, event.altKey, event.ctrlKey, event.shiftKey, event.metaKey); }; eventHandlers.set(`${ptr}-${handlerID}-wheel`, handler); element.addEventListener("wheel", handler); } function removeWheelListener(ptr, handlerID) { const element = jsmemory.get(ptr); const handler = eventHandlers.get(`${ptr}-${handlerID}-wheel`); element.removeEventListener("wheel", handler); eventHandlers.delete(`${ptr}-${handlerID}-wheel`); } let env = { freeJs: freeJs, getElementById: getElementById, setInnerHTML: setInnerHTML, setStyle: function(ptr, style, styleLength) { jsmemory.get(ptr).style.cssText = decoder.decode(new Int8Array(window.crafter_webbuild_wasi.instance.exports.memory.buffer, style, styleLength)); }, setProperty: function(ptr, property, propertyLength, value, valueLength) { const prop = decoder.decode(new Int8Array(window.crafter_webbuild_wasi.instance.exports.memory.buffer, property, propertyLength)); const val = decoder.decode(new Int8Array(window.crafter_webbuild_wasi.instance.exports.memory.buffer, value, valueLength)); jsmemory.get(ptr).style.setProperty(prop, val); }, addClass: function(ptr, className, classNameLength) { const cls = decoder.decode(new Int8Array(window.crafter_webbuild_wasi.instance.exports.memory.buffer, className, classNameLength)); jsmemory.get(ptr).classList.add(cls); }, removeClass: function(ptr, className, classNameLength) { const cls = decoder.decode(new Int8Array(window.crafter_webbuild_wasi.instance.exports.memory.buffer, className, classNameLength)); jsmemory.get(ptr).classList.remove(cls); }, toggleClass: function(ptr, className, classNameLength) { const cls = decoder.decode(new Int8Array(window.crafter_webbuild_wasi.instance.exports.memory.buffer, className, classNameLength)); jsmemory.get(ptr).classList.toggle(cls); }, hasClass: function(ptr, className, classNameLength) { const cls = decoder.decode(new Int8Array(window.crafter_webbuild_wasi.instance.exports.memory.buffer, className, classNameLength)); return jsmemory.get(ptr).classList.contains(cls); }, deleteElement: function(ptr) { const element = jsmemory.get(ptr); if(element && element.parentNode) { element.parentNode.removeChild(element); } }, addClickListener: addClickListener, removeClickListener: removeClickListener, addMouseOverListener: addMouseOverListener, removeMouseOverListener: removeMouseOverListener, addMouseOutListener: addMouseOutListener, removeMouseOutListener: removeMouseOutListener, addMouseMoveListener: addMouseMoveListener, removeMouseMoveListener: removeMouseMoveListener, addMouseDownListener: addMouseDownListener, removeMouseDownListener: removeMouseDownListener, addMouseUpListener: addMouseUpListener, removeMouseUpListener: removeMouseUpListener, addFocusListener: addFocusListener, removeFocusListener: removeFocusListener, addBlurListener: addBlurListener, removeBlurListener: removeBlurListener, addKeyDownListener: addKeyDownListener, removeKeyDownListener: removeKeyDownListener, addKeyUpListener: addKeyUpListener, removeKeyUpListener: removeKeyUpListener, addKeyPressListener: addKeyPressListener, removeKeyPressListener: removeKeyPressListener, addChangeListener: addChangeListener, removeChangeListener: removeChangeListener, addSubmitListener: addSubmitListener, removeSubmitListener: removeSubmitListener, addInputListener: addInputListener, removeInputListener: removeInputListener, addResizeListener: addResizeListener, removeResizeListener: removeResizeListener, addScrollListener: addScrollListener, removeScrollListener: removeScrollListener, addContextMenuListener: addContextMenuListener, removeContextMenuListener: removeContextMenuListener, addDragStartListener: addDragStartListener, removeDragStartListener: removeDragStartListener, addDragEndListener: addDragEndListener, removeDragEndListener: removeDragEndListener, addDropListener: addDropListener, removeDropListener: removeDropListener, addWheelListener: addWheelListener, removeWheelListener: removeWheelListener, addDragOverListener: addDragOverListener, removeDragOverListener: removeDragOverListener, addDragEnterListener: addDragEnterListener, removeDragEnterListener: removeDragEnterListener, addDragLeaveListener: addDragLeaveListener, removeDragLeaveListener: removeDragLeaveListener, } window.crafter_webbuild_env = env;