diff --git a/docs/docusaurus.config.ts b/docs/docusaurus.config.ts index 0948428..c2fe598 100644 --- a/docs/docusaurus.config.ts +++ b/docs/docusaurus.config.ts @@ -137,6 +137,7 @@ const config: Config = { prism: { theme: prismThemes.github, darkTheme: prismThemes.dracula, + additionalLanguages: ['lua'], }, } satisfies Preset.ThemeConfig, }; diff --git a/flumi/Scripts/B9/CSSParser.gd b/flumi/Scripts/B9/CSSParser.gd index 4c745ac..93f699a 100644 --- a/flumi/Scripts/B9/CSSParser.gd +++ b/flumi/Scripts/B9/CSSParser.gd @@ -112,7 +112,7 @@ class CSSStylesheet: match rule.selector_type: "simple": - return matches_simple_selector(rule.selector_parts[0], tag_name, cls_names) + return matches_simple_selector_with_element(rule.selector_parts[0], element) "descendant": return matches_descendant_selector(rule.selector_parts, element) "child": @@ -130,16 +130,33 @@ class CSSStylesheet: if selector.begins_with("."): var cls = selector.substr(1) return cls in cls_names + elif selector.begins_with("#"): + # need access to the element to check its ID + return false # will be handled by matches_simple_selector_with_element else: return selector == tag_name + func matches_simple_selector_with_element(selector: String, element: HTMLParser.HTMLElement) -> bool: + if not element: + return false + + if selector.begins_with("."): + var cls = selector.substr(1) + var cls_names = HTMLParser.extract_class_names(element) + return cls in cls_names + elif selector.begins_with("#"): + var element_id = selector.substr(1) + return element.get_attribute("id") == element_id + else: + return selector == element.tag_name + func matches_descendant_selector(parts: Array, element: HTMLParser.HTMLElement) -> bool: if not element or parts.size() < 2: return false # Last part should match current element var last_part = parts[-1].strip_edges() - if not matches_simple_selector(last_part, element.tag_name, get_element_class_names(element)): + if not matches_simple_selector_with_element(last_part, element): return false # Check ancestors for remaining parts @@ -148,7 +165,7 @@ class CSSStylesheet: while current_element and part_index >= 0: var part = parts[part_index].strip_edges() - if matches_simple_selector(part, current_element.tag_name, get_element_class_names(current_element)): + if matches_simple_selector_with_element(part, current_element): part_index -= 1 if part_index < 0: return true @@ -164,11 +181,11 @@ class CSSStylesheet: var parent_part = parts[0].strip_edges() # Element must match the child part - if not matches_simple_selector(child_part, element.tag_name, get_element_class_names(element)): + if not matches_simple_selector_with_element(child_part, element): return false # Parent must match the parent part - return matches_simple_selector(parent_part, element.parent.tag_name, get_element_class_names(element.parent)) + return matches_simple_selector_with_element(parent_part, element.parent) func matches_adjacent_sibling_selector(parts: Array, element: HTMLParser.HTMLElement) -> bool: if not element or not element.parent or parts.size() != 2: @@ -177,7 +194,7 @@ class CSSStylesheet: var second_part = parts[1].strip_edges() var first_part = parts[0].strip_edges() - if not matches_simple_selector(second_part, element.tag_name, get_element_class_names(element)): + if not matches_simple_selector_with_element(second_part, element): return false # Find previous sibling @@ -187,7 +204,7 @@ class CSSStylesheet: return false var prev_sibling = siblings[element_index - 1] - return matches_simple_selector(first_part, prev_sibling.tag_name, get_element_class_names(prev_sibling)) + return matches_simple_selector_with_element(first_part, prev_sibling) func matches_general_sibling_selector(parts: Array, element: HTMLParser.HTMLElement) -> bool: if not element or not element.parent or parts.size() != 2: @@ -196,7 +213,7 @@ class CSSStylesheet: var second_part = parts[1].strip_edges() var first_part = parts[0].strip_edges() - if not matches_simple_selector(second_part, element.tag_name, get_element_class_names(element)): + if not matches_simple_selector_with_element(second_part, element): return false # Check all previous siblings @@ -205,7 +222,7 @@ class CSSStylesheet: for i in range(element_index): var sibling = siblings[i] - if matches_simple_selector(first_part, sibling.tag_name, get_element_class_names(sibling)): + if matches_simple_selector_with_element(first_part, sibling): return true return false @@ -218,7 +235,7 @@ class CSSStylesheet: var attribute_part = parts[1].strip_edges() # Check if element matches - if element_part != "" and not matches_simple_selector(element_part, tag_name, cls_names): + if element_part != "" and not matches_simple_selector_with_element(element_part, element): return false # Parse attribute condition @@ -261,17 +278,6 @@ class CSSStylesheet: attr_value = attr_value.substr(1, attr_value.length() - 2) return {"name": attr_name, "value": attr_value} - - func get_element_class_names(element: HTMLParser.HTMLElement) -> Array[String]: - var class_names: Array[String] = [] - var class_attr = element.get_attribute("class") - if class_attr.length() > 0: - var classes = class_attr.split(" ") - for cls in classes: - cls = cls.strip_edges() - if cls.length() > 0: - class_names.append(cls) - return class_names var stylesheet: CSSStylesheet var css_text: String diff --git a/flumi/Scripts/B9/HTMLParser.gd b/flumi/Scripts/B9/HTMLParser.gd index f745aea..59d8274 100644 --- a/flumi/Scripts/B9/HTMLParser.gd +++ b/flumi/Scripts/B9/HTMLParser.gd @@ -142,7 +142,7 @@ func get_element_styles_with_inheritance(element: HTMLElement, event: String = " var styles = {} - var class_names = get_css_class_names(element) + var class_names = extract_class_names(element) styles.merge(parse_result.css_parser.stylesheet.get_styles_for_element(element.tag_name, event, class_names, element)) # Apply inline styles (higher priority) - force override CSS rules var inline_style = element.get_attribute("style") @@ -169,7 +169,7 @@ func get_element_styles_internal(element: HTMLElement, event: String = "") -> Di # Apply CSS rules if parse_result.css_parser: - var class_names = get_css_class_names(element) + var class_names = extract_class_names(element) styles.merge(parse_result.css_parser.stylesheet.get_styles_for_element(element.tag_name, event, class_names, element)) # Apply inline styles (higher priority) - force override CSS rules @@ -227,18 +227,7 @@ func parse_inline_style_with_event(style_string: String, event: String = "") -> return properties -func get_css_class_names(element: HTMLElement) -> Array[String]: - var class_names: Array[String] = [] - var class_attr = element.get_attribute("class") - if class_attr.length() > 0: - var classes = class_attr.split(" ") - for cls in classes: - cls = cls.strip_edges() - if cls.length() > 0: - class_names.append(cls) - return class_names - -func extract_class_names_from_style(element: HTMLElement) -> Array[String]: +static func extract_class_names(element: HTMLElement) -> Array[String]: var class_names: Array[String] = [] var style_attr = element.get_attribute("style") if style_attr.length() > 0: @@ -281,8 +270,10 @@ func find_all_by_class(tag: String, the_class_name: String) -> Array[HTMLElement var results: Array[HTMLElement] = [] for element in parse_result.all_elements: - if element.tag_name == tag and element.get_class_name() == the_class_name: - results.append(element) + if element.tag_name == tag: + var class_names = extract_class_names(element) + if the_class_name in class_names: + results.append(element) return results diff --git a/flumi/Scripts/B9/Lua.gd b/flumi/Scripts/B9/Lua.gd index 0b9e210..66803c7 100644 --- a/flumi/Scripts/B9/Lua.gd +++ b/flumi/Scripts/B9/Lua.gd @@ -43,21 +43,18 @@ func get_or_assign_element_id(element: HTMLParser.HTMLElement) -> String: func _gurt_select_handler(vm: LuauVM) -> int: var selector: String = vm.luaL_checkstring(1) - var element_id = "" - if selector.begins_with("#"): - element_id = selector.substr(1) - else: + var element = SelectorUtils.find_first_matching(selector, dom_parser.parse_result.all_elements) + if not element: vm.lua_pushnil() return 1 - var dom_node = dom_parser.parse_result.dom_nodes.get(element_id, null) - if not dom_node: - vm.lua_pushnil() - return 1 + var element_id = get_or_assign_element_id(element) vm.lua_newtable() 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) return 1 @@ -66,26 +63,7 @@ func _gurt_select_handler(vm: LuauVM) -> int: func _gurt_select_all_handler(vm: LuauVM) -> int: var selector: String = vm.luaL_checkstring(1) - var elements: Array[HTMLParser.HTMLElement] = [] - - # Handle different selector types - if selector.begins_with("#"): - # ID selector - find single element - var element_id = selector.substr(1) - var element = dom_parser.find_by_id(element_id) - if element: - elements.append(element) - LuaPrintUtils.lua_print_direct("WARNING: Using ID selector in select_all is not recommended, use select instead.") - elif selector.begins_with("."): - # Class selector - find all elements with class - var cls = selector.substr(1) - for element in dom_parser.parse_result.all_elements: - var element_classes = CSSParser.smart_split_utility_classes(element.get_attribute("style")) - if cls in element_classes: - elements.append(element) - else: - # Tag selector - find all elements with tag name - elements = dom_parser.find_all(selector) + var elements = SelectorUtils.find_all_matching(selector, dom_parser.parse_result.all_elements) vm.lua_newtable() var index = 1 diff --git a/flumi/Scripts/Constants.gd b/flumi/Scripts/Constants.gd index ebecbf7..52d1798 100644 --- a/flumi/Scripts/Constants.gd +++ b/flumi/Scripts/Constants.gd @@ -823,7 +823,11 @@ var HTML_CONTENT = """ local mySignal = Signal.new() local dataSignal = Signal.new() local userActionSignal = Signal.new() - + print(".container > div: ", gurt.selectAll('.container > div')) + print(".container div: ", gurt.selectAll('.container div')) + print("button[disabled]: ", gurt.selectAll('button[disabled]')) + print(".container: ", gurt.selectAll('.container')) + print("#log-area: ", gurt.selectAll('#log-area')) -- Get UI elements local logArea = gurt.select('#log-area') local statusDisplay = gurt.select('#status-display') @@ -984,6 +988,8 @@ var HTML_CONTENT = """
  • Argument Passing: Signals can pass multiple arguments to connected callbacks
  • + + """.to_utf8_buffer() diff --git a/flumi/Scripts/Utils/SelectorUtils.gd b/flumi/Scripts/Utils/SelectorUtils.gd new file mode 100644 index 0000000..fe6d9a4 --- /dev/null +++ b/flumi/Scripts/Utils/SelectorUtils.gd @@ -0,0 +1,30 @@ +class_name SelectorUtils +extends RefCounted + +static func match_element(selector: String, element: HTMLParser.HTMLElement) -> bool: + if not element: + return false + + var rule = CSSParser.CSSRule.new() + rule.init(selector) + + var class_names = HTMLParser.extract_class_names(element) + + var stylesheet = CSSParser.CSSStylesheet.new() + return stylesheet.selector_matches(rule, element.tag_name, "", class_names, element) + +static func find_all_matching(selector: String, elements: Array[HTMLParser.HTMLElement]) -> Array[HTMLParser.HTMLElement]: + var matches: Array[HTMLParser.HTMLElement] = [] + + for element in elements: + if match_element(selector, element): + matches.append(element) + + return matches + +static func find_first_matching(selector: String, elements: Array[HTMLParser.HTMLElement]) -> HTMLParser.HTMLElement: + for element in elements: + if match_element(selector, element): + return element + + return null \ No newline at end of file diff --git a/flumi/Scripts/Utils/SelectorUtils.gd.uid b/flumi/Scripts/Utils/SelectorUtils.gd.uid new file mode 100644 index 0000000..89fcea1 --- /dev/null +++ b/flumi/Scripts/Utils/SelectorUtils.gd.uid @@ -0,0 +1 @@ +uid://ce151ks0vlr2v