threaded Lua VM
This commit is contained in:
@@ -94,40 +94,78 @@ static func get_element_last_child_handler(vm: LuauVM, dom_parser: HTMLParser, l
|
||||
return 1
|
||||
|
||||
# DOM Manipulation Methods
|
||||
static func insert_before_handler(vm: LuauVM, dom_parser: HTMLParser, lua_api) -> int:
|
||||
vm.luaL_checktype(1, vm.LUA_TTABLE) # parent
|
||||
vm.luaL_checktype(2, vm.LUA_TTABLE) # new_child
|
||||
vm.luaL_checktype(3, vm.LUA_TTABLE) # reference_child
|
||||
|
||||
static func handle_element_append(operation: Dictionary, dom_parser: HTMLParser, lua_api) -> void:
|
||||
var parent_id: String = operation.parent_id
|
||||
var child_id: String = operation.child_id
|
||||
|
||||
# Get parent element info
|
||||
vm.lua_getfield(1, "_element_id")
|
||||
var parent_element_id: String = vm.lua_tostring(-1)
|
||||
vm.lua_pop(1)
|
||||
# Find the parent and child elements
|
||||
var parent_element = dom_parser.find_by_id(parent_id) if parent_id != "body" else dom_parser.find_first("body")
|
||||
var child_element = dom_parser.find_by_id(child_id) if child_id != "body" else dom_parser.find_first("body")
|
||||
|
||||
# Get new child element info
|
||||
vm.lua_getfield(2, "_element_id")
|
||||
var new_child_element_id: String = vm.lua_tostring(-1)
|
||||
vm.lua_pop(1)
|
||||
if not parent_element or not child_element:
|
||||
return
|
||||
|
||||
# Get reference child element info
|
||||
vm.lua_getfield(3, "_element_id")
|
||||
var reference_child_element_id: String = vm.lua_tostring(-1)
|
||||
vm.lua_pop(1)
|
||||
# Remove child from its current parent if it has one
|
||||
if child_element.parent:
|
||||
var current_parent = child_element.parent
|
||||
var current_index = current_parent.children.find(child_element)
|
||||
if current_index >= 0:
|
||||
current_parent.children.remove_at(current_index)
|
||||
|
||||
# Find elements
|
||||
var parent_element = find_element_by_id(parent_element_id, dom_parser)
|
||||
var new_child_element = find_element_by_id(new_child_element_id, dom_parser)
|
||||
var reference_child_element = find_element_by_id(reference_child_element_id, dom_parser)
|
||||
# Append child to new parent
|
||||
child_element.parent = parent_element
|
||||
parent_element.children.append(child_element)
|
||||
|
||||
# Handle visual rendering if parent is already rendered
|
||||
var parent_dom_node: Node = null
|
||||
if parent_id == "body":
|
||||
var main_scene = lua_api.get_node("/root/Main")
|
||||
if main_scene:
|
||||
parent_dom_node = main_scene.website_container
|
||||
else:
|
||||
parent_dom_node = dom_parser.parse_result.dom_nodes.get(parent_id, null)
|
||||
|
||||
if parent_dom_node:
|
||||
# Render the appended element
|
||||
render_new_element.call_deferred(child_element, parent_dom_node, dom_parser, lua_api)
|
||||
|
||||
static func handle_element_remove(operation: Dictionary, dom_parser: HTMLParser) -> void:
|
||||
var element_id: String = operation.element_id
|
||||
|
||||
var element = dom_parser.find_by_id(element_id) if element_id != "body" else dom_parser.find_first("body")
|
||||
if element and element.parent:
|
||||
# Remove element from HTML parser tree
|
||||
var parent_element = element.parent
|
||||
var element_index = parent_element.children.find(element)
|
||||
if element_index >= 0:
|
||||
parent_element.children.remove_at(element_index)
|
||||
element.parent = null
|
||||
|
||||
# Remove element from DOM tree
|
||||
var dom_node = dom_parser.parse_result.dom_nodes.get(element_id, null)
|
||||
if dom_node and dom_node.get_parent():
|
||||
dom_node.get_parent().remove_child(dom_node)
|
||||
dom_node.queue_free()
|
||||
dom_parser.parse_result.dom_nodes.erase(element_id)
|
||||
|
||||
# Remove from parser's all_elements list
|
||||
var all_elements_index = dom_parser.parse_result.all_elements.find(element)
|
||||
if all_elements_index >= 0:
|
||||
dom_parser.parse_result.all_elements.remove_at(all_elements_index)
|
||||
|
||||
static func handle_insert_before(operation: Dictionary, dom_parser: HTMLParser, lua_api) -> void:
|
||||
var parent_id: String = operation.parent_id
|
||||
var new_child_id: String = operation.new_child_id
|
||||
var reference_child_id: String = operation.reference_child_id
|
||||
|
||||
# Find the elements
|
||||
var parent_element = dom_parser.find_by_id(parent_id) if parent_id != "body" else dom_parser.find_first("body")
|
||||
var new_child_element = dom_parser.find_by_id(new_child_id) if new_child_id != "body" else dom_parser.find_first("body")
|
||||
var reference_child_element = dom_parser.find_by_id(reference_child_id) if reference_child_id != "body" else dom_parser.find_first("body")
|
||||
|
||||
if not parent_element or not new_child_element or not reference_child_element:
|
||||
vm.lua_pushnil()
|
||||
return 1
|
||||
|
||||
# Find reference child index in parent's children
|
||||
var reference_index = parent_element.children.find(reference_child_element)
|
||||
if reference_index < 0:
|
||||
vm.lua_pushnil()
|
||||
return 1
|
||||
return
|
||||
|
||||
# Remove new child from its current parent if it has one
|
||||
if new_child_element.parent:
|
||||
@@ -136,51 +174,37 @@ static func insert_before_handler(vm: LuauVM, dom_parser: HTMLParser, lua_api) -
|
||||
if current_index >= 0:
|
||||
current_parent.children.remove_at(current_index)
|
||||
|
||||
# Insert new child before reference child
|
||||
new_child_element.parent = parent_element
|
||||
parent_element.children.insert(reference_index, new_child_element)
|
||||
|
||||
# Handle visual rendering if parent is already rendered
|
||||
handle_visual_insertion_by_reference(parent_element_id, new_child_element, reference_child_element_id, true, dom_parser, lua_api)
|
||||
|
||||
# Return the new child
|
||||
vm.lua_pushvalue(2)
|
||||
return 1
|
||||
# Find reference child position in parent
|
||||
var reference_index = parent_element.children.find(reference_child_element)
|
||||
if reference_index >= 0:
|
||||
# Insert new child before reference child
|
||||
new_child_element.parent = parent_element
|
||||
parent_element.children.insert(reference_index, new_child_element)
|
||||
|
||||
# Handle visual rendering
|
||||
var parent_dom_node: Node = null
|
||||
if parent_id == "body":
|
||||
var main_scene = lua_api.get_node("/root/Main")
|
||||
if main_scene:
|
||||
parent_dom_node = main_scene.website_container
|
||||
else:
|
||||
parent_dom_node = dom_parser.parse_result.dom_nodes.get(parent_id, null)
|
||||
|
||||
if parent_dom_node:
|
||||
handle_visual_insertion_by_reference(parent_id, new_child_element, reference_child_id, true, dom_parser, lua_api)
|
||||
|
||||
static func insert_after_handler(vm: LuauVM, dom_parser: HTMLParser, lua_api) -> int:
|
||||
vm.luaL_checktype(1, vm.LUA_TTABLE) # parent
|
||||
vm.luaL_checktype(2, vm.LUA_TTABLE) # new_child
|
||||
vm.luaL_checktype(3, vm.LUA_TTABLE) # reference_child
|
||||
static func handle_insert_after(operation: Dictionary, dom_parser: HTMLParser, lua_api) -> void:
|
||||
var parent_id: String = operation.parent_id
|
||||
var new_child_id: String = operation.new_child_id
|
||||
var reference_child_id: String = operation.reference_child_id
|
||||
|
||||
# Get parent element info
|
||||
vm.lua_getfield(1, "_element_id")
|
||||
var parent_element_id: String = vm.lua_tostring(-1)
|
||||
vm.lua_pop(1)
|
||||
|
||||
# Get new child element info
|
||||
vm.lua_getfield(2, "_element_id")
|
||||
var new_child_element_id: String = vm.lua_tostring(-1)
|
||||
vm.lua_pop(1)
|
||||
|
||||
# Get reference child element info
|
||||
vm.lua_getfield(3, "_element_id")
|
||||
var reference_child_element_id: String = vm.lua_tostring(-1)
|
||||
vm.lua_pop(1)
|
||||
|
||||
# Find elements
|
||||
var parent_element = find_element_by_id(parent_element_id, dom_parser)
|
||||
var new_child_element = find_element_by_id(new_child_element_id, dom_parser)
|
||||
var reference_child_element = find_element_by_id(reference_child_element_id, dom_parser)
|
||||
# Find the elements
|
||||
var parent_element = dom_parser.find_by_id(parent_id) if parent_id != "body" else dom_parser.find_first("body")
|
||||
var new_child_element = dom_parser.find_by_id(new_child_id) if new_child_id != "body" else dom_parser.find_first("body")
|
||||
var reference_child_element = dom_parser.find_by_id(reference_child_id) if reference_child_id != "body" else dom_parser.find_first("body")
|
||||
|
||||
if not parent_element or not new_child_element or not reference_child_element:
|
||||
vm.lua_pushnil()
|
||||
return 1
|
||||
|
||||
# Find reference child index in parent's children
|
||||
var reference_index = parent_element.children.find(reference_child_element)
|
||||
if reference_index < 0:
|
||||
vm.lua_pushnil()
|
||||
return 1
|
||||
return
|
||||
|
||||
# Remove new child from its current parent if it has one
|
||||
if new_child_element.parent:
|
||||
@@ -189,51 +213,37 @@ static func insert_after_handler(vm: LuauVM, dom_parser: HTMLParser, lua_api) ->
|
||||
if current_index >= 0:
|
||||
current_parent.children.remove_at(current_index)
|
||||
|
||||
# Insert new child after reference child
|
||||
new_child_element.parent = parent_element
|
||||
parent_element.children.insert(reference_index + 1, new_child_element)
|
||||
|
||||
# Handle visual rendering if parent is already rendered
|
||||
handle_visual_insertion_by_reference(parent_element_id, new_child_element, reference_child_element_id, false, dom_parser, lua_api)
|
||||
|
||||
# Return the new child
|
||||
vm.lua_pushvalue(2)
|
||||
return 1
|
||||
# Find reference child position in parent
|
||||
var reference_index = parent_element.children.find(reference_child_element)
|
||||
if reference_index >= 0:
|
||||
# Insert new child after reference child
|
||||
new_child_element.parent = parent_element
|
||||
parent_element.children.insert(reference_index + 1, new_child_element)
|
||||
|
||||
# Handle visual rendering
|
||||
var parent_dom_node: Node = null
|
||||
if parent_id == "body":
|
||||
var main_scene = lua_api.get_node("/root/Main")
|
||||
if main_scene:
|
||||
parent_dom_node = main_scene.website_container
|
||||
else:
|
||||
parent_dom_node = dom_parser.parse_result.dom_nodes.get(parent_id, null)
|
||||
|
||||
if parent_dom_node:
|
||||
handle_visual_insertion_by_reference(parent_id, new_child_element, reference_child_id, false, dom_parser, lua_api)
|
||||
|
||||
static func replace_handler(vm: LuauVM, dom_parser: HTMLParser, lua_api) -> int:
|
||||
vm.luaL_checktype(1, vm.LUA_TTABLE) # parent
|
||||
vm.luaL_checktype(2, vm.LUA_TTABLE) # new_child
|
||||
vm.luaL_checktype(3, vm.LUA_TTABLE) # old_child
|
||||
static func handle_replace_child(operation: Dictionary, dom_parser: HTMLParser, lua_api) -> void:
|
||||
var parent_id: String = operation.parent_id
|
||||
var new_child_id: String = operation.new_child_id
|
||||
var old_child_id: String = operation.old_child_id
|
||||
|
||||
# Get parent element info
|
||||
vm.lua_getfield(1, "_element_id")
|
||||
var parent_element_id: String = vm.lua_tostring(-1)
|
||||
vm.lua_pop(1)
|
||||
|
||||
# Get new child element info
|
||||
vm.lua_getfield(2, "_element_id")
|
||||
var new_child_element_id: String = vm.lua_tostring(-1)
|
||||
vm.lua_pop(1)
|
||||
|
||||
# Get old child element info
|
||||
vm.lua_getfield(3, "_element_id")
|
||||
var old_child_element_id: String = vm.lua_tostring(-1)
|
||||
vm.lua_pop(1)
|
||||
|
||||
# Find elements
|
||||
var parent_element = find_element_by_id(parent_element_id, dom_parser)
|
||||
var new_child_element = find_element_by_id(new_child_element_id, dom_parser)
|
||||
var old_child_element = find_element_by_id(old_child_element_id, dom_parser)
|
||||
# Find the elements
|
||||
var parent_element = dom_parser.find_by_id(parent_id) if parent_id != "body" else dom_parser.find_first("body")
|
||||
var new_child_element = dom_parser.find_by_id(new_child_id) if new_child_id != "body" else dom_parser.find_first("body")
|
||||
var old_child_element = dom_parser.find_by_id(old_child_id) if old_child_id != "body" else dom_parser.find_first("body")
|
||||
|
||||
if not parent_element or not new_child_element or not old_child_element:
|
||||
vm.lua_pushnil()
|
||||
return 1
|
||||
|
||||
# Find old child index in parent's children
|
||||
var old_index = parent_element.children.find(old_child_element)
|
||||
if old_index < 0:
|
||||
vm.lua_pushnil()
|
||||
return 1
|
||||
return
|
||||
|
||||
# Remove new child from its current parent if it has one
|
||||
if new_child_element.parent:
|
||||
@@ -242,46 +252,43 @@ static func replace_handler(vm: LuauVM, dom_parser: HTMLParser, lua_api) -> int:
|
||||
if current_index >= 0:
|
||||
current_parent.children.remove_at(current_index)
|
||||
|
||||
# Replace old child with new child
|
||||
old_child_element.parent = null
|
||||
new_child_element.parent = parent_element
|
||||
parent_element.children[old_index] = new_child_element
|
||||
|
||||
# Handle visual updates
|
||||
handle_visual_replacement(old_child_element_id, new_child_element, parent_element_id, dom_parser, lua_api)
|
||||
|
||||
# Return the old child
|
||||
vm.lua_pushvalue(3)
|
||||
return 1
|
||||
# Find old child position in parent
|
||||
var old_index = parent_element.children.find(old_child_element)
|
||||
if old_index >= 0:
|
||||
# Replace old child with new child
|
||||
new_child_element.parent = parent_element
|
||||
parent_element.children[old_index] = new_child_element
|
||||
old_child_element.parent = null
|
||||
|
||||
# Handle visual rendering
|
||||
handle_visual_replacement(old_child_id, new_child_element, parent_id, dom_parser, lua_api)
|
||||
|
||||
static func clone_handler(vm: LuauVM, dom_parser: HTMLParser, lua_api) -> int:
|
||||
vm.luaL_checktype(1, vm.LUA_TTABLE) # element to clone
|
||||
var deep: bool = false
|
||||
static func render_new_element(element: HTMLParser.HTMLElement, parent_node: Node, dom_parser: HTMLParser, lua_api) -> void:
|
||||
# Get reference to main scene for rendering
|
||||
var main_scene = lua_api.get_node("/root/Main")
|
||||
if not main_scene:
|
||||
return
|
||||
|
||||
if vm.lua_gettop() >= 2:
|
||||
deep = vm.lua_toboolean(2)
|
||||
|
||||
# Get element info
|
||||
vm.lua_getfield(1, "_element_id")
|
||||
var element_id: String = vm.lua_tostring(-1)
|
||||
vm.lua_pop(1)
|
||||
|
||||
# Find the element
|
||||
var element = find_element_by_id(element_id, dom_parser)
|
||||
if not element:
|
||||
vm.lua_pushnil()
|
||||
return 1
|
||||
|
||||
# Clone the element
|
||||
var cloned_element = clone_element(element, deep)
|
||||
|
||||
# Add cloned element to parser's element collection
|
||||
dom_parser.parse_result.all_elements.append(cloned_element)
|
||||
|
||||
# Create Lua element wrapper with full functionality
|
||||
create_element_wrapper(vm, cloned_element, lua_api)
|
||||
|
||||
return 1
|
||||
# Create the visual node for the element
|
||||
var element_node = await main_scene.create_element_node(element, dom_parser)
|
||||
if not element_node:
|
||||
LuaPrintUtils.lua_print_direct("Failed to create visual node for element: " + str(element))
|
||||
return
|
||||
|
||||
# Set metadata so ul/ol can detect dynamically added li elements
|
||||
element_node.set_meta("html_element", element)
|
||||
|
||||
# Register the DOM node
|
||||
dom_parser.register_dom_node(element, element_node)
|
||||
|
||||
# Add to parent - handle body special case
|
||||
var container_node = parent_node
|
||||
if parent_node is MarginContainer and parent_node.get_child_count() > 0:
|
||||
container_node = parent_node.get_child(0)
|
||||
elif parent_node == main_scene.website_container:
|
||||
container_node = parent_node
|
||||
|
||||
main_scene.safe_add_child(container_node, element_node)
|
||||
|
||||
# Helper functions
|
||||
static func find_element_by_id(element_id: String, dom_parser: HTMLParser) -> HTMLParser.HTMLElement:
|
||||
@@ -290,26 +297,6 @@ static func find_element_by_id(element_id: String, dom_parser: HTMLParser) -> HT
|
||||
else:
|
||||
return dom_parser.find_by_id(element_id)
|
||||
|
||||
static func create_element_wrapper(vm: LuauVM, element: HTMLParser.HTMLElement, lua_api: LuaAPI) -> void:
|
||||
vm.lua_newtable()
|
||||
|
||||
var element_id: String
|
||||
if element.tag_name == "body":
|
||||
element_id = "body"
|
||||
else:
|
||||
element_id = element.get_attribute("id")
|
||||
if element_id.is_empty():
|
||||
element_id = lua_api.get_or_assign_element_id(element)
|
||||
element.set_attribute("id", element_id)
|
||||
|
||||
vm.lua_pushstring(element_id)
|
||||
vm.lua_setfield(-2, "_element_id")
|
||||
vm.lua_pushstring(element.tag_name)
|
||||
vm.lua_setfield(-2, "_tag_name")
|
||||
|
||||
if lua_api:
|
||||
lua_api.add_element_methods(vm)
|
||||
|
||||
static func clone_element(element: HTMLParser.HTMLElement, deep: bool) -> HTMLParser.HTMLElement:
|
||||
var cloned = HTMLParser.HTMLElement.new(element.tag_name)
|
||||
|
||||
@@ -370,18 +357,7 @@ static func handle_visual_replacement(old_child_element_id: String, new_child_el
|
||||
if parent_dom_node:
|
||||
render_new_element_at_position.call_deferred(new_child_element, parent_dom_node, old_position, dom_parser)
|
||||
|
||||
static func add_enhanced_element_methods(vm: LuauVM, lua_api, index: String = "element") -> void:
|
||||
vm.lua_pushcallable(lua_api._element_insert_before_wrapper, index + ".insertBefore")
|
||||
vm.lua_setfield(-2, "insertBefore")
|
||||
|
||||
vm.lua_pushcallable(lua_api._element_insert_after_wrapper, index + ".insertAfter")
|
||||
vm.lua_setfield(-2, "insertAfter")
|
||||
|
||||
vm.lua_pushcallable(lua_api._element_replace_wrapper, index + ".replace")
|
||||
vm.lua_setfield(-2, "replace")
|
||||
|
||||
vm.lua_pushcallable(lua_api._element_clone_wrapper, index + ".clone")
|
||||
vm.lua_setfield(-2, "clone")
|
||||
|
||||
|
||||
static func is_same_element_visual_node(node1: Node, node2: Node) -> bool:
|
||||
if node1 == node2:
|
||||
@@ -475,20 +451,531 @@ static func render_new_element_by_reference(element: HTMLParser.HTMLElement, par
|
||||
if insert_position >= 0 and insert_position < container_node.get_child_count() - 1:
|
||||
container_node.move_child(element_node, insert_position)
|
||||
|
||||
# Threaded-safe wrapper functions
|
||||
static func emit_dom_operation(lua_api: LuaAPI, operation: Dictionary) -> void:
|
||||
lua_api.threaded_vm.call_deferred("_emit_dom_operation_request", operation)
|
||||
|
||||
static func create_element_wrapper(vm: LuauVM, element: HTMLParser.HTMLElement, lua_api: LuaAPI) -> void:
|
||||
vm.lua_newtable()
|
||||
|
||||
var element_id: String
|
||||
if element.tag_name == "body":
|
||||
element_id = "body"
|
||||
else:
|
||||
element_id = element.get_attribute("id")
|
||||
if element_id.is_empty():
|
||||
element_id = lua_api.get_or_assign_element_id(element)
|
||||
element.set_attribute("id", element_id)
|
||||
|
||||
vm.lua_pushstring(element_id)
|
||||
vm.lua_setfield(-2, "_element_id")
|
||||
vm.lua_pushstring(element.tag_name)
|
||||
vm.lua_setfield(-2, "_tag_name")
|
||||
|
||||
add_element_methods(vm, lua_api)
|
||||
|
||||
static func add_element_methods(vm: LuauVM, lua_api: LuaAPI) -> void:
|
||||
vm.set_meta("lua_api", lua_api)
|
||||
|
||||
vm.lua_pushcallable(Callable(LuaDOMUtils, "_element_on_wrapper"), "element.on")
|
||||
vm.lua_setfield(-2, "on")
|
||||
|
||||
vm.lua_pushcallable(Callable(LuaDOMUtils, "_element_append_wrapper"), "element.append")
|
||||
vm.lua_setfield(-2, "append")
|
||||
|
||||
vm.lua_pushcallable(Callable(LuaDOMUtils, "_element_set_text_wrapper"), "element.setText")
|
||||
vm.lua_setfield(-2, "setText")
|
||||
|
||||
vm.lua_pushcallable(Callable(LuaDOMUtils, "_element_remove_wrapper"), "element.remove")
|
||||
vm.lua_setfield(-2, "remove")
|
||||
|
||||
vm.lua_pushcallable(Callable(LuaDOMUtils, "_element_insert_before_wrapper"), "element.insertBefore")
|
||||
vm.lua_setfield(-2, "insertBefore")
|
||||
|
||||
vm.lua_pushcallable(Callable(LuaDOMUtils, "_element_insert_after_wrapper"), "element.insertAfter")
|
||||
vm.lua_setfield(-2, "insertAfter")
|
||||
|
||||
vm.lua_pushcallable(Callable(LuaDOMUtils, "_element_replace_wrapper"), "element.replace")
|
||||
vm.lua_setfield(-2, "replace")
|
||||
|
||||
vm.lua_pushcallable(Callable(LuaDOMUtils, "_element_clone_wrapper"), "element.clone")
|
||||
vm.lua_setfield(-2, "clone")
|
||||
|
||||
_add_classlist_support(vm, lua_api)
|
||||
|
||||
vm.lua_newtable()
|
||||
vm.lua_pushcallable(Callable(LuaDOMUtils, "_element_index_wrapper"), "element.__index")
|
||||
vm.lua_setfield(-2, "__index")
|
||||
vm.lua_pushcallable(Callable(LuaDOMUtils, "_element_newindex_wrapper"), "element.__newindex")
|
||||
vm.lua_setfield(-2, "__newindex")
|
||||
vm.lua_setmetatable(-2)
|
||||
|
||||
static func _element_on_wrapper(vm: LuauVM) -> int:
|
||||
var lua_api = vm.get_meta("lua_api") as LuaAPI
|
||||
if not lua_api:
|
||||
return 0
|
||||
|
||||
vm.luaL_checktype(1, vm.LUA_TTABLE)
|
||||
var event_name: String = vm.luaL_checkstring(2)
|
||||
vm.luaL_checktype(3, vm.LUA_TFUNCTION)
|
||||
|
||||
vm.lua_getfield(1, "_element_id")
|
||||
var element_id: String = vm.lua_tostring(-1)
|
||||
vm.lua_pop(1)
|
||||
|
||||
var callback_ref = lua_api.next_callback_ref
|
||||
lua_api.next_callback_ref += 1
|
||||
|
||||
var subscription_id = lua_api.next_subscription_id
|
||||
lua_api.next_subscription_id += 1
|
||||
|
||||
vm.lua_pushvalue(3)
|
||||
vm.lua_rawseti(vm.LUA_REGISTRYINDEX, callback_ref)
|
||||
|
||||
lua_api.call_deferred("_register_event_on_main_thread", element_id, event_name, callback_ref, subscription_id)
|
||||
|
||||
vm.lua_newtable()
|
||||
vm.lua_pushinteger(subscription_id)
|
||||
vm.lua_setfield(-2, "_subscription_id")
|
||||
|
||||
vm.lua_pushcallable(Callable(LuaDOMUtils, "_unsubscribe_wrapper"), "subscription.unsubscribe")
|
||||
vm.lua_setfield(-2, "unsubscribe")
|
||||
return 1
|
||||
|
||||
static func _element_append_wrapper(vm: LuauVM) -> int:
|
||||
var lua_api = vm.get_meta("lua_api") as LuaAPI
|
||||
if not lua_api:
|
||||
return 0
|
||||
|
||||
# Queue append operation for main thread
|
||||
vm.luaL_checktype(1, vm.LUA_TTABLE) # parent
|
||||
vm.luaL_checktype(2, vm.LUA_TTABLE) # child
|
||||
|
||||
vm.lua_getfield(1, "_element_id")
|
||||
var parent_id: String = vm.lua_tostring(-1)
|
||||
vm.lua_pop(1)
|
||||
|
||||
vm.lua_getfield(2, "_element_id")
|
||||
var child_id: String = vm.lua_tostring(-1)
|
||||
vm.lua_pop(1)
|
||||
|
||||
var operation = {
|
||||
"type": "append_element",
|
||||
"parent_id": parent_id,
|
||||
"child_id": child_id
|
||||
}
|
||||
|
||||
emit_dom_operation(lua_api, operation)
|
||||
return 0
|
||||
|
||||
static func _element_set_text_wrapper(vm: LuauVM) -> int:
|
||||
var lua_api = vm.get_meta("lua_api") as LuaAPI
|
||||
if not lua_api:
|
||||
return 0
|
||||
|
||||
vm.luaL_checktype(1, vm.LUA_TTABLE)
|
||||
var text: String = vm.luaL_checkstring(2)
|
||||
|
||||
vm.lua_getfield(1, "_element_id")
|
||||
var element_id: String = vm.lua_tostring(-1)
|
||||
vm.lua_pop(1)
|
||||
|
||||
var element = lua_api.dom_parser.find_by_id(element_id) if element_id != "body" else lua_api.dom_parser.find_first("body")
|
||||
if element:
|
||||
element.text_content = text
|
||||
|
||||
var operation = {
|
||||
"type": "set_text",
|
||||
"selector": "#" + element_id,
|
||||
"text": text
|
||||
}
|
||||
|
||||
emit_dom_operation(lua_api, operation)
|
||||
return 0
|
||||
|
||||
static func _element_remove_wrapper(vm: LuauVM) -> int:
|
||||
var lua_api = vm.get_meta("lua_api") as LuaAPI
|
||||
if not lua_api:
|
||||
return 0
|
||||
|
||||
# Get element ID from self table
|
||||
vm.luaL_checktype(1, vm.LUA_TTABLE)
|
||||
vm.lua_getfield(1, "_element_id")
|
||||
var element_id: String = vm.lua_tostring(-1)
|
||||
vm.lua_pop(1)
|
||||
|
||||
var operation = {
|
||||
"type": "remove_element",
|
||||
"element_id": element_id
|
||||
}
|
||||
|
||||
# Queue operation for main thread
|
||||
emit_dom_operation(lua_api, operation)
|
||||
return 0
|
||||
|
||||
static func _element_insert_before_wrapper(vm: LuauVM) -> int:
|
||||
var lua_api = vm.get_meta("lua_api") as LuaAPI
|
||||
if not lua_api:
|
||||
return 0
|
||||
|
||||
# Get parent element ID from self table
|
||||
vm.luaL_checktype(1, vm.LUA_TTABLE) # parent
|
||||
vm.luaL_checktype(2, vm.LUA_TTABLE) # new_child
|
||||
vm.luaL_checktype(3, vm.LUA_TTABLE) # reference_child
|
||||
|
||||
vm.lua_getfield(1, "_element_id")
|
||||
var parent_id: String = vm.lua_tostring(-1)
|
||||
vm.lua_pop(1)
|
||||
|
||||
vm.lua_getfield(2, "_element_id")
|
||||
var new_child_id: String = vm.lua_tostring(-1)
|
||||
vm.lua_pop(1)
|
||||
|
||||
vm.lua_getfield(3, "_element_id")
|
||||
var reference_child_id: String = vm.lua_tostring(-1)
|
||||
vm.lua_pop(1)
|
||||
|
||||
var operation = {
|
||||
"type": "insert_before",
|
||||
"parent_id": parent_id,
|
||||
"new_child_id": new_child_id,
|
||||
"reference_child_id": reference_child_id
|
||||
}
|
||||
|
||||
emit_dom_operation(lua_api, operation)
|
||||
return 0
|
||||
|
||||
static func _element_insert_after_wrapper(vm: LuauVM) -> int:
|
||||
var lua_api = vm.get_meta("lua_api") as LuaAPI
|
||||
if not lua_api:
|
||||
return 0
|
||||
|
||||
# Get parent element ID from self table
|
||||
vm.luaL_checktype(1, vm.LUA_TTABLE) # parent
|
||||
vm.luaL_checktype(2, vm.LUA_TTABLE) # new_child
|
||||
vm.luaL_checktype(3, vm.LUA_TTABLE) # reference_child
|
||||
|
||||
vm.lua_getfield(1, "_element_id")
|
||||
var parent_id: String = vm.lua_tostring(-1)
|
||||
vm.lua_pop(1)
|
||||
|
||||
vm.lua_getfield(2, "_element_id")
|
||||
var new_child_id: String = vm.lua_tostring(-1)
|
||||
vm.lua_pop(1)
|
||||
|
||||
vm.lua_getfield(3, "_element_id")
|
||||
var reference_child_id: String = vm.lua_tostring(-1)
|
||||
vm.lua_pop(1)
|
||||
|
||||
var operation = {
|
||||
"type": "insert_after",
|
||||
"parent_id": parent_id,
|
||||
"new_child_id": new_child_id,
|
||||
"reference_child_id": reference_child_id
|
||||
}
|
||||
|
||||
emit_dom_operation(lua_api, operation)
|
||||
return 0
|
||||
|
||||
static func _element_replace_wrapper(vm: LuauVM) -> int:
|
||||
var lua_api = vm.get_meta("lua_api") as LuaAPI
|
||||
if not lua_api:
|
||||
return 0
|
||||
|
||||
# Get parent element ID from self table
|
||||
vm.luaL_checktype(1, vm.LUA_TTABLE) # parent
|
||||
vm.luaL_checktype(2, vm.LUA_TTABLE) # new_child
|
||||
vm.luaL_checktype(3, vm.LUA_TTABLE) # old_child
|
||||
|
||||
vm.lua_getfield(1, "_element_id")
|
||||
var parent_id: String = vm.lua_tostring(-1)
|
||||
vm.lua_pop(1)
|
||||
|
||||
vm.lua_getfield(2, "_element_id")
|
||||
var new_child_id: String = vm.lua_tostring(-1)
|
||||
vm.lua_pop(1)
|
||||
|
||||
vm.lua_getfield(3, "_element_id")
|
||||
var old_child_id: String = vm.lua_tostring(-1)
|
||||
vm.lua_pop(1)
|
||||
|
||||
var operation = {
|
||||
"type": "replace_child",
|
||||
"parent_id": parent_id,
|
||||
"new_child_id": new_child_id,
|
||||
"old_child_id": old_child_id
|
||||
}
|
||||
|
||||
emit_dom_operation(lua_api, operation)
|
||||
return 0
|
||||
|
||||
static func _element_clone_wrapper(vm: LuauVM) -> int:
|
||||
var lua_api = vm.get_meta("lua_api") as LuaAPI
|
||||
if not lua_api:
|
||||
return 0
|
||||
|
||||
# Get element ID from self table
|
||||
vm.luaL_checktype(1, vm.LUA_TTABLE) # element
|
||||
var deep: bool = true # Default to deep clone
|
||||
|
||||
if vm.lua_gettop() >= 2:
|
||||
deep = vm.lua_toboolean(2)
|
||||
|
||||
vm.lua_getfield(1, "_element_id")
|
||||
var element_id: String = vm.lua_tostring(-1)
|
||||
vm.lua_pop(1)
|
||||
|
||||
# Find the element to clone
|
||||
var element = lua_api.dom_parser.find_by_id(element_id) if element_id != "body" else lua_api.dom_parser.find_first("body")
|
||||
if element:
|
||||
var cloned_element = clone_element(element, deep)
|
||||
|
||||
# Assign new ID to cloned element
|
||||
var new_id = lua_api.get_or_assign_element_id(cloned_element)
|
||||
|
||||
# Add to parser's element collection
|
||||
lua_api.dom_parser.parse_result.all_elements.append(cloned_element)
|
||||
|
||||
# Create element wrapper for the cloned element
|
||||
create_element_wrapper(vm, cloned_element, lua_api)
|
||||
return 1
|
||||
|
||||
vm.lua_pushnil()
|
||||
return 1
|
||||
|
||||
static func _element_index_wrapper(vm: LuauVM) -> int:
|
||||
vm.luaL_checktype(1, vm.LUA_TTABLE)
|
||||
var key: String = vm.luaL_checkstring(2)
|
||||
|
||||
match key:
|
||||
"text":
|
||||
# Get lua_api from VM metadata
|
||||
var lua_api = vm.get_meta("lua_api") as LuaAPI
|
||||
if lua_api:
|
||||
# Get element ID and find the element
|
||||
vm.lua_getfield(1, "_element_id")
|
||||
var element_id: String = vm.lua_tostring(-1)
|
||||
vm.lua_pop(1)
|
||||
|
||||
var element = lua_api.dom_parser.find_by_id(element_id) if element_id != "body" else lua_api.dom_parser.find_first("body")
|
||||
if element:
|
||||
vm.lua_pushstring(element.text_content)
|
||||
return 1
|
||||
|
||||
# Fallback to empty string
|
||||
vm.lua_pushstring("")
|
||||
return 1
|
||||
"children":
|
||||
# Get lua_api from VM metadata
|
||||
var lua_api = vm.get_meta("lua_api") as LuaAPI
|
||||
if lua_api:
|
||||
# Get element ID and find the element
|
||||
vm.lua_getfield(1, "_element_id")
|
||||
var element_id: String = vm.lua_tostring(-1)
|
||||
vm.lua_pop(1)
|
||||
|
||||
var element = lua_api.dom_parser.find_by_id(element_id) if element_id != "body" else lua_api.dom_parser.find_first("body")
|
||||
if element:
|
||||
# Create array of child elements
|
||||
vm.lua_newtable()
|
||||
var index = 1
|
||||
for child in element.children:
|
||||
create_element_wrapper(vm, child, lua_api)
|
||||
vm.lua_rawseti(-2, index)
|
||||
index += 1
|
||||
return 1
|
||||
|
||||
# Fallback to empty array
|
||||
vm.lua_newtable()
|
||||
return 1
|
||||
_:
|
||||
# Check for DOM traversal properties first
|
||||
var lua_api = vm.get_meta("lua_api") as LuaAPI
|
||||
if lua_api:
|
||||
match key:
|
||||
"parent":
|
||||
return get_element_parent_handler(vm, lua_api.dom_parser, lua_api)
|
||||
"nextSibling":
|
||||
return get_element_next_sibling_handler(vm, lua_api.dom_parser, lua_api)
|
||||
"previousSibling":
|
||||
return get_element_previous_sibling_handler(vm, lua_api.dom_parser, lua_api)
|
||||
"firstChild":
|
||||
return get_element_first_child_handler(vm, lua_api.dom_parser, lua_api)
|
||||
"lastChild":
|
||||
return get_element_last_child_handler(vm, lua_api.dom_parser, lua_api)
|
||||
|
||||
# Check if it's a method in the original table
|
||||
vm.lua_pushvalue(1)
|
||||
vm.lua_pushstring(key)
|
||||
vm.lua_rawget(-2)
|
||||
vm.lua_remove(-2)
|
||||
return 1
|
||||
|
||||
static func _add_classlist_support(vm: LuauVM, lua_api: LuaAPI) -> void:
|
||||
# Create classList table with threaded methods
|
||||
vm.lua_newtable()
|
||||
|
||||
# Store the element_id in the classList table
|
||||
vm.lua_getfield(-2, "_element_id") # Get element_id from parent element
|
||||
vm.lua_setfield(-2, "_element_id") # Store it in classList table
|
||||
|
||||
# Add classList methods
|
||||
vm.lua_pushcallable(Callable(LuaDOMUtils, "_classlist_add_wrapper"), "classList.add")
|
||||
vm.lua_setfield(-2, "add")
|
||||
|
||||
vm.lua_pushcallable(Callable(LuaDOMUtils, "_classlist_remove_wrapper"), "classList.remove")
|
||||
vm.lua_setfield(-2, "remove")
|
||||
|
||||
vm.lua_pushcallable(Callable(LuaDOMUtils, "_classlist_toggle_wrapper"), "classList.toggle")
|
||||
vm.lua_setfield(-2, "toggle")
|
||||
|
||||
# Set classList on the element
|
||||
vm.lua_setfield(-2, "classList")
|
||||
|
||||
static func _classlist_add_wrapper(vm: LuauVM) -> int:
|
||||
# Get lua_api from VM metadata
|
||||
var lua_api = vm.get_meta("lua_api") as LuaAPI
|
||||
if not lua_api:
|
||||
return 0
|
||||
|
||||
vm.luaL_checktype(1, vm.LUA_TTABLE) # classList table
|
||||
var cls: String = vm.luaL_checkstring(2)
|
||||
|
||||
# Get element_id from classList table
|
||||
vm.lua_getfield(1, "_element_id")
|
||||
var element_id: String = vm.lua_tostring(-1)
|
||||
vm.lua_pop(1)
|
||||
|
||||
var operation = {
|
||||
"type": "add_class",
|
||||
"element_id": element_id,
|
||||
"class_name": cls
|
||||
}
|
||||
|
||||
emit_dom_operation(lua_api, operation)
|
||||
return 0
|
||||
|
||||
static func _classlist_remove_wrapper(vm: LuauVM) -> int:
|
||||
# Get lua_api from VM metadata
|
||||
var lua_api = vm.get_meta("lua_api") as LuaAPI
|
||||
if not lua_api:
|
||||
return 0
|
||||
|
||||
vm.luaL_checktype(1, vm.LUA_TTABLE) # classList table
|
||||
var cls: String = vm.luaL_checkstring(2)
|
||||
|
||||
# Get element_id from classList table
|
||||
vm.lua_getfield(1, "_element_id")
|
||||
var element_id: String = vm.lua_tostring(-1)
|
||||
vm.lua_pop(1)
|
||||
|
||||
var operation = {
|
||||
"type": "remove_class",
|
||||
"element_id": element_id,
|
||||
"class_name": cls
|
||||
}
|
||||
|
||||
emit_dom_operation(lua_api, operation)
|
||||
return 0
|
||||
|
||||
static func _classlist_toggle_wrapper(vm: LuauVM) -> int:
|
||||
# Get lua_api from VM metadata
|
||||
var lua_api = vm.get_meta("lua_api") as LuaAPI
|
||||
if not lua_api:
|
||||
return 0
|
||||
|
||||
vm.luaL_checktype(1, vm.LUA_TTABLE) # classList table
|
||||
var cls: String = vm.luaL_checkstring(2)
|
||||
|
||||
# Get element_id from classList table
|
||||
vm.lua_getfield(1, "_element_id")
|
||||
var element_id: String = vm.lua_tostring(-1)
|
||||
vm.lua_pop(1)
|
||||
|
||||
var operation = {
|
||||
"type": "toggle_class",
|
||||
"element_id": element_id,
|
||||
"class_name": cls
|
||||
}
|
||||
|
||||
emit_dom_operation(lua_api, operation)
|
||||
return 0
|
||||
|
||||
static func _element_newindex_wrapper(vm: LuauVM) -> int:
|
||||
# Get lua_api from VM metadata
|
||||
var lua_api = vm.get_meta("lua_api") as LuaAPI
|
||||
if not lua_api:
|
||||
return 0
|
||||
|
||||
vm.luaL_checktype(1, vm.LUA_TTABLE)
|
||||
var key: String = vm.luaL_checkstring(2)
|
||||
var value = vm.lua_tovariant(3)
|
||||
|
||||
match key:
|
||||
"text":
|
||||
var text: String = str(value) # Convert value to string
|
||||
|
||||
vm.lua_getfield(1, "_element_id")
|
||||
var element_id: String = vm.lua_tostring(-1)
|
||||
vm.lua_pop(1)
|
||||
|
||||
var element = lua_api.dom_parser.find_by_id(element_id) if element_id != "body" else lua_api.dom_parser.find_first("body")
|
||||
if element:
|
||||
element.text_content = text
|
||||
|
||||
# Also queue the DOM operation for visual updates if the element is already rendered
|
||||
var operation = {
|
||||
"type": "set_text",
|
||||
"selector": "#" + element_id,
|
||||
"text": text
|
||||
}
|
||||
|
||||
emit_dom_operation(lua_api, operation)
|
||||
return 0
|
||||
_:
|
||||
# Store in table normally
|
||||
vm.lua_pushvalue(2)
|
||||
vm.lua_pushvalue(3)
|
||||
vm.lua_rawset(1)
|
||||
return 0
|
||||
|
||||
static func _unsubscribe_wrapper(vm: LuauVM) -> int:
|
||||
# Get subscription ID from the subscription table
|
||||
vm.luaL_checktype(1, vm.LUA_TTABLE)
|
||||
|
||||
vm.lua_getfield(1, "_subscription_id")
|
||||
var subscription_id: int = vm.lua_tointeger(-1)
|
||||
vm.lua_pop(1)
|
||||
|
||||
# Get lua_api from VM metadata
|
||||
var lua_api = vm.get_meta("lua_api") as LuaAPI
|
||||
if not lua_api:
|
||||
vm.lua_pushboolean(false)
|
||||
return 1
|
||||
|
||||
# Handle unsubscribe on main thread
|
||||
if subscription_id > 0:
|
||||
lua_api.call_deferred("_unsubscribe_event_on_main_thread", subscription_id)
|
||||
vm.lua_pushboolean(true)
|
||||
else:
|
||||
vm.lua_pushboolean(false)
|
||||
|
||||
return 1
|
||||
|
||||
static func _index_handler(vm: LuauVM, lua_api: LuaAPI) -> int:
|
||||
vm.luaL_checktype(1, vm.LUA_TTABLE)
|
||||
var key: String = vm.luaL_checkstring(2)
|
||||
|
||||
match key:
|
||||
"parent":
|
||||
return lua_api._get_element_parent_wrapper(vm, lua_api)
|
||||
return get_element_parent_handler(vm, lua_api.dom_parser, lua_api)
|
||||
"nextSibling":
|
||||
return lua_api._get_element_next_sibling_wrapper(vm, lua_api)
|
||||
return get_element_next_sibling_handler(vm, lua_api.dom_parser, lua_api)
|
||||
"previousSibling":
|
||||
return lua_api._get_element_previous_sibling_wrapper(vm, lua_api)
|
||||
return get_element_previous_sibling_handler(vm, lua_api.dom_parser, lua_api)
|
||||
"firstChild":
|
||||
return lua_api._get_element_first_child_wrapper(vm, lua_api)
|
||||
return get_element_first_child_handler(vm, lua_api.dom_parser, lua_api)
|
||||
"lastChild":
|
||||
return lua_api._get_element_last_child_wrapper(vm, lua_api)
|
||||
return get_element_last_child_handler(vm, lua_api.dom_parser, lua_api)
|
||||
_:
|
||||
return lua_api._element_index_handler(vm)
|
||||
return _element_index_wrapper(vm)
|
||||
|
||||
Reference in New Issue
Block a user