add search engine - ringle

This commit is contained in:
Face
2025-08-27 20:23:05 +03:00
parent 1cf81bbfee
commit 347b40ed71
47 changed files with 7214 additions and 493 deletions

View File

@@ -6,8 +6,9 @@ class CSSRule:
var event_prefix: String = ""
var properties: Dictionary = {}
var specificity: int = 0
var selector_type: String = "simple" # simple, descendant, child, adjacent_sibling, general_sibling, attribute
var selector_parts: Array = [] # For complex selectors
var selector_type: String = "simple" # simple, descendant, child, adjacent_sibling, general_sibling, attribute
var selector_parts: Array = [] # For complex selectors
var is_user_css: bool = false
func init(sel: String = ""):
selector = sel
@@ -52,9 +53,9 @@ class CSSRule:
func calculate_specificity():
specificity = 1
if selector.begins_with("."):
specificity += 10
specificity += 20
if selector.contains("["):
specificity += 10 # Attribute selectors
specificity += 10
match selector_type:
"child":
specificity += 8
@@ -68,6 +69,10 @@ class CSSRule:
specificity += 4
if event_prefix.length() > 0:
specificity += 10
if is_user_css:
specificity += 100
class CSSStylesheet:
var rules: Array[CSSRule] = []
@@ -287,7 +292,7 @@ func init(css_content: String = ""):
stylesheet = CSSStylesheet.new()
css_text = css_content
func parse() -> void:
func parse(is_user_css: bool = false) -> void:
if css_text.is_empty():
return
@@ -295,7 +300,7 @@ func parse() -> void:
var rules = extract_rules(cleaned_css)
for rule_data in rules:
var rule = parse_rule(rule_data)
var rule = parse_rule(rule_data, is_user_css)
if rule:
stylesheet.add_rule(rule)
@@ -355,9 +360,10 @@ func find_matching_brace(css: String, start_pos: int) -> int:
return -1
func parse_rule(rule_data: Dictionary) -> CSSRule:
func parse_rule(rule_data: Dictionary, is_user_css: bool = false) -> CSSRule:
var rule = CSSRule.new()
rule.selector = rule_data.selector
rule.is_user_css = is_user_css
rule.init(rule.selector)
var properties_text = rule_data.properties
@@ -397,6 +403,7 @@ func parse_utility_class(rule: CSSRule, utility_name: String) -> void:
pseudo_rule.event_prefix = pseudo
pseudo_rule.selector_type = rule.selector_type
pseudo_rule.selector_parts = rule.selector_parts.duplicate()
pseudo_rule.is_user_css = rule.is_user_css
pseudo_rule.calculate_specificity()
pseudo_rule.specificity += 100

View File

@@ -25,20 +25,20 @@ class HTMLElement:
return get_attribute("id")
func get_collapsed_text() -> String:
var collapsed = text_content.strip_edges()
var collapsed = HTMLParser.unescape_html_entities(text_content).strip_edges()
# Replace multiple whitespace characters with single space
var regex = RegEx.new()
regex.compile("\\s+")
return regex.sub(collapsed, " ", true)
func get_preserved_text() -> String:
return text_content
return HTMLParser.unescape_html_entities(text_content)
func get_bbcode_formatted_text(parser: HTMLParser) -> String:
var styles = {}
if parser != null:
styles = parser.get_element_styles_with_inheritance(self, "", [])
return HTMLParser.get_bbcode_with_styles(self, styles, parser)
return HTMLParser.get_bbcode_with_styles(self, styles, parser, [])
func is_inline_element() -> bool:
return tag_name in ["b", "i", "u", "small", "mark", "code", "span", "a", "input"]
@@ -68,10 +68,69 @@ var bitcode: PackedByteArray
var parse_result: ParseResult
func _init(data: PackedByteArray):
bitcode = data
var html_string = data.get_string_from_utf8()
html_string = preprocess_html_entities(html_string)
bitcode = html_string.to_utf8_buffer()
xml_parser = XMLParser.new()
parse_result = ParseResult.new()
static func unescape_html_entities(text: String) -> String:
return text.replace("&lt;", "<").replace("&gt;", ">").replace("&quot;", "\"").replace("&#39;", "'").replace("&amp;", "&")
static func preprocess_html_entities(html: String) -> String:
var result = ""
var i = 0
var in_tag = false
while i < html.length():
var char = html[i]
if char == "<":
# Check if this starts a valid HTML tag
var tag_end = html.find(">", i)
if tag_end != -1:
var potential_tag = html.substr(i, tag_end - i + 1)
# Simple check for valid tag pattern
if is_valid_tag_pattern(potential_tag):
result += potential_tag
i = tag_end + 1
continue
# If not a valid tag, escape it
result += "&lt;"
elif char == ">":
# Escape standalone > that's not part of a tag
result += "&gt;"
else:
result += char
i += 1
return result
static func is_valid_tag_pattern(tag: String) -> bool:
if tag.length() < 3: # Minimum: <x>
return false
if not tag.begins_with("<") or not tag.ends_with(">"):
return false
var inner = tag.substr(1, tag.length() - 2).strip_edges()
if inner.begins_with("/"):
inner = inner.substr(1).strip_edges()
# Handle self-closing tags
if inner.ends_with("/"):
inner = inner.substr(0, inner.length() - 1).strip_edges()
# Extract tag name (first part before space or attributes)
var tag_name = inner.split(" ")[0].split("\t")[0]
# Valid tag names contain only letters, numbers, and hyphens
var regex = RegEx.new()
regex.compile("^[a-zA-Z][a-zA-Z0-9-]*$")
return regex.search(tag_name) != null
# Main parsing function
func parse() -> ParseResult:
xml_parser.open_buffer(bitcode)
@@ -408,7 +467,7 @@ func apply_element_styles(node: Control, element: HTMLElement, parser: HTMLParse
var styles = parser.get_element_styles_with_inheritance(element, "", [])
if node.get("rich_text_label"):
var label = node.rich_text_label
var text = HTMLParser.get_bbcode_with_styles(element, styles, parser)
var text = HTMLParser.get_bbcode_with_styles(element, styles, parser, [])
label.text = text
static func apply_element_bbcode_formatting(element: HTMLElement, styles: Dictionary, content: String, parser: HTMLParser = null) -> String:
@@ -478,7 +537,13 @@ static func apply_element_bbcode_formatting(element: HTMLElement, styles: Dictio
return formatted_content
static func get_bbcode_with_styles(element: HTMLElement, styles: Dictionary, parser: HTMLParser) -> String:
static func get_bbcode_with_styles(element: HTMLElement, styles: Dictionary, parser: HTMLParser, visited_elements: Array = []) -> String:
if element in visited_elements:
return ""
var new_visited = visited_elements.duplicate()
new_visited.append(element)
var text = ""
if element.text_content.length() > 0:
text += element.get_collapsed_text()
@@ -486,8 +551,8 @@ static func get_bbcode_with_styles(element: HTMLElement, styles: Dictionary, par
for child in element.children:
var child_styles = styles
if parser != null:
child_styles = parser.get_element_styles_with_inheritance(child, "", [])
var child_content = HTMLParser.get_bbcode_with_styles(child, child_styles, parser)
child_styles = parser.get_element_styles_with_inheritance(child, "", new_visited)
var child_content = HTMLParser.get_bbcode_with_styles(child, child_styles, parser, new_visited)
child_content = apply_element_bbcode_formatting(child, child_styles, child_content, parser)
text += child_content

View File

@@ -642,8 +642,6 @@ func execute_lua_script(code: String, vm: LuauVM):
script_start_time = Time.get_ticks_msec() / 1000.0
threaded_vm.execute_script_async(code)
func _on_threaded_script_completed(result: Dictionary):
var execution_time = (Time.get_ticks_msec() / 1000.0) - script_start_time
@@ -689,6 +687,10 @@ func _handle_dom_operation(operation: Dictionary):
LuaDOMUtils.handle_insert_after(operation, dom_parser, self)
"replace_child":
LuaDOMUtils.handle_replace_child(operation, dom_parser, self)
"focus_element":
_handle_element_focus(operation)
"unfocus_element":
_handle_element_unfocus(operation)
_:
pass # Unknown operation type, ignore
@@ -786,6 +788,53 @@ func _handle_text_getting(operation: Dictionary):
return element.text_content
return ""
func _handle_element_focus(operation: Dictionary):
var element_id: String = operation.element_id
var dom_node = dom_parser.parse_result.dom_nodes.get(element_id, null)
if not dom_node:
return
var focusable_control = _find_focusable_control(dom_node)
if focusable_control and focusable_control.has_method("grab_focus"):
focusable_control.call_deferred("grab_focus")
func _handle_element_unfocus(operation: Dictionary):
var element_id: String = operation.element_id
var dom_node = dom_parser.parse_result.dom_nodes.get(element_id, null)
if not dom_node:
return
var focusable_control = _find_focusable_control(dom_node)
if focusable_control and focusable_control.has_method("release_focus"):
focusable_control.call_deferred("release_focus")
func _find_focusable_control(node: Node) -> Control:
if not node:
return null
if node is Control and node.focus_mode != Control.FOCUS_NONE and node.has_method("grab_focus"):
return node
if node.has_method("get_children"):
for child in node.get_children():
if child.visible and child is Control:
if child is LineEdit or child is TextEdit or child is SpinBox or child is OptionButton:
if child.focus_mode != Control.FOCUS_NONE:
return child
if child is SpinBox:
var line_edit = child.get_line_edit()
if line_edit and line_edit.focus_mode != Control.FOCUS_NONE:
return line_edit
var focusable_child = _find_focusable_control(child)
if focusable_child:
return focusable_child
return null
func _handle_body_event_registration(operation: Dictionary):
var event_name: String = operation.event_name
var callback_ref: int = operation.callback_ref