-module(agnostic@dev@query). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/agnostic/dev/query.gleam"). -export([element/1, child/2, descendant/2, 'and'/2, tag/1, namespaced/2, attribute/2, class/1, style/2, id/1, data/2, test_id/1, aria/2, text/1, matches/2, find_path/4, find/2, find_all/2, has/2, to_readable_string/1]). -export_type(['query'/0, selector/0]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. -opaque 'query'() :: {find_element, selector()} | {find_child, 'query'(), selector()} | {find_descendant, 'query'(), selector()}. -opaque selector() :: {all, list(selector())} | {type, binary(), binary()} | {has_attribute, binary(), binary()} | {has_class, binary()} | {has_style, binary(), binary()} | {has_text, binary()}. -file("src/agnostic/dev/query.gleam", 44). ?DOC(" Find any elements in a view that match the given [`Selector`](#Selector).\n"). -spec element(selector()) -> 'query'(). element(Selector) -> {find_element, Selector}. -file("src/agnostic/dev/query.gleam", 52). ?DOC( " Given a `Query` that finds an element, find any of that element's _direct_\n" " children that match the given [`Selector`](#Selector). This is similar to the\n" " CSS `>` combinator.\n" ). -spec child('query'(), selector()) -> 'query'(). child(Parent, Selector) -> {find_child, Parent, Selector}. -file("src/agnostic/dev/query.gleam", 60). ?DOC( " Given a `Query` that finds an element, find any of that element's _descendants_\n" " that match the given [`Selector`](#Selector). This will walk the entire tree\n" " from the matching parent.\n" ). -spec descendant('query'(), selector()) -> 'query'(). descendant(Parent, Selector) -> {find_descendant, Parent, Selector}. -file("src/agnostic/dev/query.gleam", 98). ?DOC( " Combine two selectors into one that must match both. For example, if you have\n" " a selector for div elements and a selector for elements with the class \"wibble\"\n" " then they can be combined into a selector that matches only div elements with\n" " the class \"wibble\".\n" "\n" " ```gleam\n" " import agnostic/dev/query\n" "\n" " pub fn example() {\n" " let div = query.tag(\"div\")\n" " let wibble = query.class(\"wibble\")\n" "\n" " query.element(matching: div |> query.and(wibble))\n" " }\n" " ```\n" "\n" " You can chain multiple `and` calls together to combine many selectors into\n" " something more specific.\n" "\n" " ```gleam\n" " import agnostic/dev/query\n" "\n" " pub fn example() {\n" " query.tag(\"div\")\n" " |> query.and(query.class(\"wibble\"))\n" " |> query.and(query.data(\"open\", \"true\"))\n" " }\n" " ```\n" "\n" " > **Note**: if you find yourself crafting complex selectors, consider using\n" " > a test id on the element(s) you want to find instead.\n" ). -spec 'and'(selector(), selector()) -> selector(). 'and'(First, Second) -> case First of {all, []} -> {all, [Second]}; {all, Others} -> {all, [Second | Others]}; _ -> {all, [First, Second]} end. -file("src/agnostic/dev/query.gleam", 110). ?DOC( " Select elements based on their tag name, like `\"div\"`, `\"span\"`, or `\"a\"`.\n" " To select elements with an XML namespace - such as SVG elements - use the\n" " [`namespaced`](#namespaced) selector instead.\n" ). -spec tag(binary()) -> selector(). tag(Value) -> {type, <<""/utf8>>, Value}. -file("src/agnostic/dev/query.gleam", 128). ?DOC( " Select elements based on their tag name and XML namespace. This is useful\n" " for selecting SVG elements or other XML elements that have a namespace.\n" " For example, to select an SVG circle element, you would use:\n" "\n" " ```gleam\n" " import agnostic/dev/query\n" "\n" " pub fn example() {\n" " let svg = \"http://www.w3.org/2000/svg\"\n" "\n" " query.element(matching: query.namespaced(svg, \"circle\"))\n" " }\n" " ```\n" ). -spec namespaced(binary(), binary()) -> selector(). namespaced(Namespace, Tag) -> {type, Namespace, Tag}. -file("src/agnostic/dev/query.gleam", 157). ?DOC( " Select elements that have the specified attribute with the given value. If\n" " the value is left blank, this selector will match any element that has the\n" " attribute, _regardless of its value_.\n" "\n" " For example, to select a form input with the name \"username\", you would\n" " use:\n" "\n" " ```gleam\n" " import agnostic/dev/query\n" "\n" " pub fn example() {\n" " query.element(matching: query.attribute(\"name\", \"username\"))\n" " }\n" " ```\n" "\n" " Or to select elements with the `disabled` attribute:\n" "\n" " ```gleam\n" " import agnostic/dev/query\n" "\n" " pub fn example() {\n" " query.element(matching: query.attribute(\"disabled\", \"\"))\n" " }\n" " ```\n" ). -spec attribute(binary(), binary()) -> selector(). attribute(Name, Value) -> {has_attribute, Name, Value}. -file("src/agnostic/dev/query.gleam", 172). ?DOC( " Select elements that include the given space-separated class name(s). For\n" " example given the element `
`, the following selectors\n" " would match:\n" "\n" " - `query.class(\"foo\")`\n" "\n" " - `query.class(\"bar baz\")`\n" "\n" " If you need to match the class attribute exactly, you can use the [`attribute`](#attribute)\n" " selector instead.\n" ). -spec class(binary()) -> selector(). class(Name) -> {has_class, Name}. -file("src/agnostic/dev/query.gleam", 180). ?DOC( " Select elements that have the specified inline style with the given value.\n" " If the value is left blank, this selector will match any element that has\n" " the given style, _regardless of its value_.\n" ). -spec style(binary(), binary()) -> selector(). style(Name, Value) -> {has_style, Name, Value}. -file("src/agnostic/dev/query.gleam", 187). ?DOC( " Select an element based on its `id` attribute. Well-formed HTML means that\n" " only one element should have a given id.\n" ). -spec id(binary()) -> selector(). id(Name) -> {has_attribute, <<"id"/utf8>>, Name}. -file("src/agnostic/dev/query.gleam", 202). ?DOC( " Select elements that have the given `data-*` attribute. For example you can\n" " select a custom disclosure element that is currently open with:\n" "\n" " ```gleam\n" " import agnostic/dev/query\n" "\n" " pub fn example() {\n" " query.element(matching: query.data(\"open\", \"true\"))\n" " }\n" " ```\n" ). -spec data(binary(), binary()) -> selector(). data(Name, Value) -> {has_attribute, <<"data-"/utf8, Name/binary>>, Value}. -file("src/agnostic/dev/query.gleam", 210). ?DOC( " It is a common convention to use the `data-test-id` attribute to mark elements\n" " for easy selection in tests. This function is a shorthand for writing\n" " `query.data(\"test-id\", value)`\n" ). -spec test_id(binary()) -> selector(). test_id(Value) -> data(<<"test-id"/utf8>>, Value). -file("src/agnostic/dev/query.gleam", 225). ?DOC( " Select elements that have the given `aria-*` attribute. For example you can\n" " select the trigger of a dropdown menu with:\n" "\n" " ```gleam\n" " import agnostic/dev/query\n" "\n" " pub fn example() {\n" " query.element(matching: query.aria(\"expanded\", \"true\"))\n" " }\n" " ```\n" ). -spec aria(binary(), binary()) -> selector(). aria(Name, Value) -> {has_attribute, <<"aria-"/utf8, Name/binary>>, Value}. -file("src/agnostic/dev/query.gleam", 257). ?DOC( " Select elements whose text content matches the given string exactly. This\n" " includes text from **inline** children, but not from **block** children. For\n" " example, given the following HTML:\n" "\n" " ```html\n" "

Hello, Joe!

\n" " ```\n" "\n" " The selector `query.text(\"Hello, Joe!\")` would match the `

` element because\n" " the text content of the inline `` element is included in the paragraph's\n" " text content.\n" "\n" " Whitespace must match exactly, so the selector `query.text(\"Hello, Joe!\")`\n" " would not match an element like:\n" "\n" " ```gleam\n" " html.p([], [html.text(\"Hello, Joe!\")])\n" " ```\n" "\n" " > **Note**: while this selector makes a best-effort attempt to include the\n" " > text content of inline children, this cannot account for block elements that\n" " > are styled as inline by CSS stylesheets.\n" "\n" " > **Note**: often it is better to use more precise selectors such as\n" " > [`id`](#id), [`class`](#class), or [`test_id`](#test_id). You should reach\n" " > for this selector only when you want to assert that an element contains\n" " > some specific text, such as in a hero banner or a copyright notice.\n" ). -spec text(binary()) -> selector(). text(Content) -> {has_text, Content}. -file("src/agnostic/dev/query.gleam", 628). -spec text_content(agnostic@vdom@vnode:element(any()), boolean(), binary()) -> binary(). text_content(Element, Inline, Content) -> case Element of {fragment, _, _, _, _} -> gleam@list:fold( erlang:element(4, Element), Content, fun(Content@1, Child) -> text_content(Child, true, Content@1) end ); {map, _, _, _, Child@1} -> text_content(Child@1, Inline, Content); {memo, _, _, _, View} -> text_content(View(), Inline, Content); {element, _, _, _, <<"area"/utf8>>, _, _, _} -> Content; {element, _, _, _, <<"base"/utf8>>, _, _, _} -> Content; {element, _, _, _, <<"col"/utf8>>, _, _, _} -> Content; {element, _, _, _, <<"embed"/utf8>>, _, _, _} -> Content; {element, _, _, _, <<"hr"/utf8>>, _, _, _} -> Content; {element, _, _, _, <<"img"/utf8>>, _, _, _} -> Content; {element, _, _, _, <<"input"/utf8>>, _, _, _} -> Content; {element, _, _, _, <<"link"/utf8>>, _, _, _} -> Content; {element, _, _, _, <<"meta"/utf8>>, _, _, _} -> Content; {element, _, _, _, <<"param"/utf8>>, _, _, _} -> Content; {element, _, _, _, <<"script"/utf8>>, _, _, _} -> Content; {element, _, _, _, <<"source"/utf8>>, _, _, _} -> Content; {element, _, _, _, <<"style"/utf8>>, _, _, _} -> Content; {element, _, _, _, <<"track"/utf8>>, _, _, _} -> Content; {element, _, _, _, <<"wbr"/utf8>>, _, _, _} -> Content; {element, _, _, _, _, _, _, _} when not Inline orelse (erlang:element( 4, Element ) =/= <<""/utf8>>) -> gleam@list:fold( erlang:element(7, Element), Content, fun(Content@2, Child@2) -> text_content(Child@2, true, Content@2) end ); {element, _, _, _, <<"br"/utf8>>, _, _, _} -> <>; {element, _, _, _, <<"a"/utf8>>, _, _, _} -> gleam@list:fold( erlang:element(7, Element), Content, fun(Content@3, Child@3) -> text_content(Child@3, true, Content@3) end ); {element, _, _, _, <<"abbr"/utf8>>, _, _, _} -> gleam@list:fold( erlang:element(7, Element), Content, fun(Content@3, Child@3) -> text_content(Child@3, true, Content@3) end ); {element, _, _, _, <<"acronym"/utf8>>, _, _, _} -> gleam@list:fold( erlang:element(7, Element), Content, fun(Content@3, Child@3) -> text_content(Child@3, true, Content@3) end ); {element, _, _, _, <<"b"/utf8>>, _, _, _} -> gleam@list:fold( erlang:element(7, Element), Content, fun(Content@3, Child@3) -> text_content(Child@3, true, Content@3) end ); {element, _, _, _, <<"bdo"/utf8>>, _, _, _} -> gleam@list:fold( erlang:element(7, Element), Content, fun(Content@3, Child@3) -> text_content(Child@3, true, Content@3) end ); {element, _, _, _, <<"big"/utf8>>, _, _, _} -> gleam@list:fold( erlang:element(7, Element), Content, fun(Content@3, Child@3) -> text_content(Child@3, true, Content@3) end ); {element, _, _, _, <<"button"/utf8>>, _, _, _} -> gleam@list:fold( erlang:element(7, Element), Content, fun(Content@3, Child@3) -> text_content(Child@3, true, Content@3) end ); {element, _, _, _, <<"cite"/utf8>>, _, _, _} -> gleam@list:fold( erlang:element(7, Element), Content, fun(Content@3, Child@3) -> text_content(Child@3, true, Content@3) end ); {element, _, _, _, <<"code"/utf8>>, _, _, _} -> gleam@list:fold( erlang:element(7, Element), Content, fun(Content@3, Child@3) -> text_content(Child@3, true, Content@3) end ); {element, _, _, _, <<"dfn"/utf8>>, _, _, _} -> gleam@list:fold( erlang:element(7, Element), Content, fun(Content@3, Child@3) -> text_content(Child@3, true, Content@3) end ); {element, _, _, _, <<"em"/utf8>>, _, _, _} -> gleam@list:fold( erlang:element(7, Element), Content, fun(Content@3, Child@3) -> text_content(Child@3, true, Content@3) end ); {element, _, _, _, <<"i"/utf8>>, _, _, _} -> gleam@list:fold( erlang:element(7, Element), Content, fun(Content@3, Child@3) -> text_content(Child@3, true, Content@3) end ); {element, _, _, _, <<"kbd"/utf8>>, _, _, _} -> gleam@list:fold( erlang:element(7, Element), Content, fun(Content@3, Child@3) -> text_content(Child@3, true, Content@3) end ); {element, _, _, _, <<"label"/utf8>>, _, _, _} -> gleam@list:fold( erlang:element(7, Element), Content, fun(Content@3, Child@3) -> text_content(Child@3, true, Content@3) end ); {element, _, _, _, <<"map"/utf8>>, _, _, _} -> gleam@list:fold( erlang:element(7, Element), Content, fun(Content@3, Child@3) -> text_content(Child@3, true, Content@3) end ); {element, _, _, _, <<"object"/utf8>>, _, _, _} -> gleam@list:fold( erlang:element(7, Element), Content, fun(Content@3, Child@3) -> text_content(Child@3, true, Content@3) end ); {element, _, _, _, <<"output"/utf8>>, _, _, _} -> gleam@list:fold( erlang:element(7, Element), Content, fun(Content@3, Child@3) -> text_content(Child@3, true, Content@3) end ); {element, _, _, _, <<"q"/utf8>>, _, _, _} -> gleam@list:fold( erlang:element(7, Element), Content, fun(Content@3, Child@3) -> text_content(Child@3, true, Content@3) end ); {element, _, _, _, <<"samp"/utf8>>, _, _, _} -> gleam@list:fold( erlang:element(7, Element), Content, fun(Content@3, Child@3) -> text_content(Child@3, true, Content@3) end ); {element, _, _, _, <<"small"/utf8>>, _, _, _} -> gleam@list:fold( erlang:element(7, Element), Content, fun(Content@3, Child@3) -> text_content(Child@3, true, Content@3) end ); {element, _, _, _, <<"span"/utf8>>, _, _, _} -> gleam@list:fold( erlang:element(7, Element), Content, fun(Content@3, Child@3) -> text_content(Child@3, true, Content@3) end ); {element, _, _, _, <<"strong"/utf8>>, _, _, _} -> gleam@list:fold( erlang:element(7, Element), Content, fun(Content@3, Child@3) -> text_content(Child@3, true, Content@3) end ); {element, _, _, _, <<"sub"/utf8>>, _, _, _} -> gleam@list:fold( erlang:element(7, Element), Content, fun(Content@3, Child@3) -> text_content(Child@3, true, Content@3) end ); {element, _, _, _, <<"sup"/utf8>>, _, _, _} -> gleam@list:fold( erlang:element(7, Element), Content, fun(Content@3, Child@3) -> text_content(Child@3, true, Content@3) end ); {element, _, _, _, <<"time"/utf8>>, _, _, _} -> gleam@list:fold( erlang:element(7, Element), Content, fun(Content@3, Child@3) -> text_content(Child@3, true, Content@3) end ); {element, _, _, _, <<"tt"/utf8>>, _, _, _} -> gleam@list:fold( erlang:element(7, Element), Content, fun(Content@3, Child@3) -> text_content(Child@3, true, Content@3) end ); {element, _, _, _, <<"var"/utf8>>, _, _, _} -> gleam@list:fold( erlang:element(7, Element), Content, fun(Content@3, Child@3) -> text_content(Child@3, true, Content@3) end ); {element, _, _, _, _, _, _, _} -> Rule = <<"display:inline"/utf8>>, Is_inline = gleam@list:any( erlang:element(6, Element), fun(Attribute) -> case Attribute of {attribute, _, <<"style"/utf8>>, Value} -> gleam_stdlib:contains_string(Value, Rule); _ -> false end end ), case Is_inline of true -> gleam@list:fold( erlang:element(7, Element), Content, fun(Content@4, Child@4) -> text_content(Child@4, true, Content@4) end ); false -> Content end; {text, _, _, _} -> <>; {raw_container, _, _, _, _, _, _, _} -> Content; {raw_node, _, _, _, _} -> Content end. -file("src/agnostic/dev/query.gleam", 556). ?DOC(" Check if the given target element matches the given [`Selector`](#Selector).\n"). -spec matches(agnostic@vdom@vnode:element(any()), selector()) -> boolean(). matches(Element, Selector) -> case {Element, Selector} of {_, {all, Selectors}} -> gleam@list:all( Selectors, fun(_capture) -> matches(Element, _capture) end ); {{element, _, _, Namespace, Tag, _, _, _}, {type, _, _}} -> (Namespace =:= erlang:element(2, Selector)) andalso (Tag =:= erlang:element( 3, Selector )); {{raw_container, _, _, Namespace, Tag, _, _, _}, {type, _, _}} -> (Namespace =:= erlang:element(2, Selector)) andalso (Tag =:= erlang:element( 3, Selector )); {{element, _, _, _, _, Attributes, _, _}, {has_attribute, Name, <<""/utf8>>}} -> gleam@list:any(Attributes, fun(Attribute) -> case Attribute of {attribute, _, _, _} -> erlang:element(3, Attribute) =:= Name; _ -> false end end); {{raw_container, _, _, _, _, Attributes, _, _}, {has_attribute, Name, <<""/utf8>>}} -> gleam@list:any(Attributes, fun(Attribute) -> case Attribute of {attribute, _, _, _} -> erlang:element(3, Attribute) =:= Name; _ -> false end end); {{element, _, _, _, _, Attributes@1, _, _}, {has_attribute, Name@1, Value}} -> gleam@list:contains( Attributes@1, agnostic@attribute:attribute(Name@1, Value) ); {{raw_container, _, _, _, _, Attributes@1, _, _}, {has_attribute, Name@1, Value}} -> gleam@list:contains( Attributes@1, agnostic@attribute:attribute(Name@1, Value) ); {{element, _, _, _, _, Attributes@2, _, _}, {has_class, Name@2}} -> gleam@list:fold_until( gleam@string:split(Name@2, <<" "/utf8>>), true, fun(_, Class) -> Name@3 = gleam@string:trim_end(Class), Matches = gleam@list:any( Attributes@2, fun(Attribute@1) -> case Attribute@1 of {attribute, _, <<"class"/utf8>>, Value@1} -> (((Value@1 =:= Name@3) orelse gleam_stdlib:string_starts_with( Value@1, <> )) orelse gleam_stdlib:string_ends_with( Value@1, <<" "/utf8, Name@3/binary>> )) orelse gleam_stdlib:contains_string( Value@1, <<<<" "/utf8, Name@3/binary>>/binary, " "/utf8>> ); _ -> false end end ), case Matches of true -> {continue, true}; false -> {stop, false} end end ); {{raw_container, _, _, _, _, Attributes@2, _, _}, {has_class, Name@2}} -> gleam@list:fold_until( gleam@string:split(Name@2, <<" "/utf8>>), true, fun(_, Class) -> Name@3 = gleam@string:trim_end(Class), Matches = gleam@list:any( Attributes@2, fun(Attribute@1) -> case Attribute@1 of {attribute, _, <<"class"/utf8>>, Value@1} -> (((Value@1 =:= Name@3) orelse gleam_stdlib:string_starts_with( Value@1, <> )) orelse gleam_stdlib:string_ends_with( Value@1, <<" "/utf8, Name@3/binary>> )) orelse gleam_stdlib:contains_string( Value@1, <<<<" "/utf8, Name@3/binary>>/binary, " "/utf8>> ); _ -> false end end ), case Matches of true -> {continue, true}; false -> {stop, false} end end ); {{element, _, _, _, _, Attributes@3, _, _}, {has_style, Name@4, Value@2}} -> Rule = <<<<<>/binary, Value@2/binary>>/binary, ";"/utf8>>, gleam@list:any(Attributes@3, fun(Attribute@2) -> case Attribute@2 of {attribute, _, <<"style"/utf8>>, Value@3} -> gleam_stdlib:contains_string(Value@3, Rule); _ -> false end end); {{raw_container, _, _, _, _, Attributes@3, _, _}, {has_style, Name@4, Value@2}} -> Rule = <<<<<>/binary, Value@2/binary>>/binary, ";"/utf8>>, gleam@list:any(Attributes@3, fun(Attribute@2) -> case Attribute@2 of {attribute, _, <<"style"/utf8>>, Value@3} -> gleam_stdlib:contains_string(Value@3, Rule); _ -> false end end); {{element, _, _, _, _, _, _, _}, {has_text, Content}} -> text_content(Element, false, <<""/utf8>>) =:= Content; {_, _} -> false end. -file("src/agnostic/dev/query.gleam", 357). -spec find_matching_in_list( list(agnostic@vdom@vnode:element(NIM)), selector(), agnostic@vdom@path:path(), integer() ) -> {ok, {agnostic@vdom@vnode:element(NIM), agnostic@vdom@path:path()}} | {error, nil}. find_matching_in_list(Elements, Selector, Path, Index) -> case Elements of [] -> {error, nil}; [{fragment, _, _, _, _} = First | Rest] -> find_matching_in_list( lists:append(erlang:element(4, First), Rest), Selector, agnostic@vdom@path:add(Path, Index, erlang:element(3, First)), 0 ); [First@1 | Rest@1] -> case matches(First@1, Selector) of true -> {ok, {First@1, agnostic@vdom@path:add( Path, Index, erlang:element(3, First@1) )}}; false -> find_matching_in_list(Rest@1, Selector, Path, Index + 1) end end. -file("src/agnostic/dev/query.gleam", 341). -spec find_direct_child( agnostic@vdom@vnode:element(NIH), selector(), agnostic@vdom@path:path() ) -> {ok, {agnostic@vdom@vnode:element(NIH), agnostic@vdom@path:path()}} | {error, nil}. find_direct_child(Parent, Selector, Path) -> case Parent of {element, _, _, _, _, _, Children, _} -> find_matching_in_list(Children, Selector, Path, 0); {fragment, _, _, Children, _} -> find_matching_in_list(Children, Selector, Path, 0); {map, _, _, _, Child} -> find_direct_child(Child, Selector, Path); {memo, _, _, _, View} -> find_direct_child(View(), Selector, Path); {raw_container, _, _, _, _, _, _, _} -> {error, nil}; {raw_node, _, _, _, _} -> {error, nil}; {text, _, _, _} -> {error, nil} end. -file("src/agnostic/dev/query.gleam", 402). -spec find_descendant_in_list( list(agnostic@vdom@vnode:element(NIX)), selector(), agnostic@vdom@path:path(), integer() ) -> {ok, {agnostic@vdom@vnode:element(NIX), agnostic@vdom@path:path()}} | {error, nil}. find_descendant_in_list(Elements, Selector, Path, Index) -> case Elements of [] -> {error, nil}; [First | Rest] -> case matches(First, Selector) of true -> {ok, {First, agnostic@vdom@path:add( Path, Index, erlang:element(3, First) )}}; false -> Child = agnostic@vdom@path:add( Path, Index, erlang:element(3, First) ), case find_descendant(First, Selector, Child) of {ok, Element} -> {ok, Element}; {error, _} -> find_descendant_in_list( Rest, Selector, Path, Index + 1 ) end end end. -file("src/agnostic/dev/query.gleam", 382). -spec find_descendant( agnostic@vdom@vnode:element(NIS), selector(), agnostic@vdom@path:path() ) -> {ok, {agnostic@vdom@vnode:element(NIS), agnostic@vdom@path:path()}} | {error, nil}. find_descendant(Parent, Selector, Path) -> case find_direct_child(Parent, Selector, Path) of {ok, Element} -> {ok, Element}; {error, _} -> case Parent of {element, _, _, _, _, _, Children, _} -> find_descendant_in_list(Children, Selector, Path, 0); {fragment, _, _, Children, _} -> find_descendant_in_list(Children, Selector, Path, 0); {map, _, _, _, Child} -> find_descendant(Child, Selector, Path); {memo, _, _, _, View} -> find_descendant(View(), Selector, Path); {raw_container, _, _, _, _, _, _, _} -> {error, nil}; {raw_node, _, _, _, _} -> {error, nil}; {text, _, _, _} -> {error, nil} end end. -file("src/agnostic/dev/query.gleam", 323). -spec find_in_list( list(agnostic@vdom@vnode:element(NIB)), 'query'(), agnostic@vdom@path:path(), integer() ) -> {ok, {agnostic@vdom@vnode:element(NIB), agnostic@vdom@path:path()}} | {error, nil}. find_in_list(Elements, Query, Path, Index) -> case Elements of [] -> {error, nil}; [First | Rest] -> case find_path(First, Query, Index, Path) of {ok, Element} -> {ok, Element}; {error, _} -> find_in_list(Rest, Query, Path, Index + 1) end end. -file("src/agnostic/dev/query.gleam", 307). -spec find_in_children( agnostic@vdom@vnode:element(NHW), 'query'(), integer(), agnostic@vdom@path:path() ) -> {ok, {agnostic@vdom@vnode:element(NHW), agnostic@vdom@path:path()}} | {error, nil}. find_in_children(Element, Query, Index, Path) -> case Element of {element, _, Key, _, _, _, Children, _} -> find_in_list( Children, Query, begin _pipe = Path, agnostic@vdom@path:add(_pipe, Index, Key) end, 0 ); {fragment, _, Key, Children, _} -> find_in_list( Children, Query, begin _pipe = Path, agnostic@vdom@path:add(_pipe, Index, Key) end, 0 ); {map, _, Key@1, _, Child} -> find_path( Child, Query, 0, agnostic@vdom@path:subtree( begin _pipe@1 = Path, agnostic@vdom@path:add(_pipe@1, Index, Key@1) end ) ); {memo, _, _, _, View} -> find_path(View(), Query, Index, Path); {raw_container, _, _, _, _, _, _, _} -> {error, nil}; {raw_node, _, _, _, _} -> {error, nil}; {text, _, _, _} -> {error, nil} end. -file("src/agnostic/dev/query.gleam", 280). ?DOC(false). -spec find_path( agnostic@vdom@vnode:element(NHR), 'query'(), integer(), agnostic@vdom@path:path() ) -> {ok, {agnostic@vdom@vnode:element(NHR), agnostic@vdom@path:path()}} | {error, nil}. find_path(Root, Query, Index, Path) -> case Query of {find_element, Selector} -> case matches(Root, Selector) of true -> {ok, {Root, begin _pipe = Path, agnostic@vdom@path:add( _pipe, Index, erlang:element(3, Root) ) end}}; false -> find_in_children(Root, Query, Index, Path) end; {find_child, Parent, Selector@1} -> case find_path(Root, Parent, Index, Path) of {ok, {Element, Path@1}} -> find_direct_child(Element, Selector@1, Path@1); {error, _} -> {error, nil} end; {find_descendant, Parent@1, Selector@2} -> case find_path(Root, Parent@1, Index, Path) of {ok, {Element@1, Path@2}} -> find_descendant(Element@1, Selector@2, Path@2); {error, _} -> {error, nil} end end. -file("src/agnostic/dev/query.gleam", 267). ?DOC( " Find the first element in a view that matches the given [`Query`](#Query).\n" " This is useful for tests when combined with [`element.to_readable_string`](../element.html#to_readable_string),\n" " allowing you to render large views but take more precise snapshots.\n" ). -spec find(agnostic@vdom@vnode:element(NHM), 'query'()) -> {ok, agnostic@vdom@vnode:element(NHM)} | {error, nil}. find(Root, Query) -> case find_path(Root, Query, 0, root) of {ok, {Element, _}} -> {ok, Element}; {error, _} -> {error, nil} end. -file("src/agnostic/dev/query.gleam", 497). -spec find_all_matching_in_list( list(agnostic@vdom@vnode:element(NJU)), selector() ) -> list(agnostic@vdom@vnode:element(NJU)). find_all_matching_in_list(Elements, Selector) -> case Elements of [] -> []; [First | Rest] -> case matches(First, Selector) of true -> [First | find_all_matching_in_list(Rest, Selector)]; false -> find_all_matching_in_list(Rest, Selector) end end. -file("src/agnostic/dev/query.gleam", 482). -spec find_all_direct_children(agnostic@vdom@vnode:element(NJQ), selector()) -> list(agnostic@vdom@vnode:element(NJQ)). find_all_direct_children(Parent, Selector) -> case Parent of {element, _, _, _, _, _, Children, _} -> find_all_matching_in_list(Children, Selector); {fragment, _, _, Children, _} -> find_all_matching_in_list(Children, Selector); {map, _, _, _, Child} -> find_all_direct_children(Child, Selector); {memo, _, _, _, View} -> find_all_direct_children(View(), Selector); {raw_container, _, _, _, _, _, _, _} -> []; {raw_node, _, _, _, _} -> []; {text, _, _, _} -> [] end. -file("src/agnostic/dev/query.gleam", 529). -spec find_all_descendants_in_list( list(agnostic@vdom@vnode:element(NKD)), selector() ) -> list(agnostic@vdom@vnode:element(NKD)). find_all_descendants_in_list(Elements, Selector) -> case Elements of [] -> []; [First | Rest] -> First_matches = find_all_descendants(First, Selector), Rest_matches = find_all_descendants_in_list(Rest, Selector), lists:append(First_matches, Rest_matches) end. -file("src/agnostic/dev/query.gleam", 511). -spec find_all_descendants(agnostic@vdom@vnode:element(NJZ), selector()) -> list(agnostic@vdom@vnode:element(NJZ)). find_all_descendants(Parent, Selector) -> Direct_matches = find_all_direct_children(Parent, Selector), Descendant_matches = case Parent of {element, _, _, _, _, _, Children, _} -> find_all_descendants_in_list(Children, Selector); {fragment, _, _, Children, _} -> find_all_descendants_in_list(Children, Selector); {map, _, _, _, Child} -> find_all_descendants(Child, Selector); {memo, _, _, _, View} -> find_all_descendants(View(), Selector); {raw_container, _, _, _, _, _, _, _} -> []; {raw_node, _, _, _, _} -> []; {text, _, _, _} -> [] end, lists:append(Direct_matches, Descendant_matches). -file("src/agnostic/dev/query.gleam", 467). -spec find_all_in_list(list(agnostic@vdom@vnode:element(NJL)), 'query'()) -> list(agnostic@vdom@vnode:element(NJL)). find_all_in_list(Elements, Query) -> case Elements of [] -> []; [First | Rest] -> First_matches = find_all(First, Query), Rest_matches = find_all_in_list(Rest, Query), lists:append(First_matches, Rest_matches) end. -file("src/agnostic/dev/query.gleam", 452). -spec find_all_in_children(agnostic@vdom@vnode:element(NJH), 'query'()) -> list(agnostic@vdom@vnode:element(NJH)). find_all_in_children(Element, Query) -> case Element of {element, _, _, _, _, _, Children, _} -> find_all_in_list(Children, Query); {fragment, _, _, Children, _} -> find_all_in_list(Children, Query); {map, _, _, _, Child} -> find_all_in_children(Child, Query); {memo, _, _, _, View} -> find_all_in_children(View(), Query); {raw_container, _, _, _, _, _, _, _} -> []; {raw_node, _, _, _, _} -> []; {text, _, _, _} -> [] end. -file("src/agnostic/dev/query.gleam", 429). ?DOC( " Like [`find`](#find) but returns every element in the view that matches the\n" " given query.\n" ). -spec find_all(agnostic@vdom@vnode:element(NJD), 'query'()) -> list(agnostic@vdom@vnode:element(NJD)). find_all(Root, Query) -> case Query of {find_element, Selector} -> case matches(Root, Selector) of true -> [Root | find_all_in_children(Root, Query)]; false -> find_all_in_children(Root, Query) end; {find_child, Parent, Selector@1} -> _pipe = Root, _pipe@1 = find_all(_pipe, Parent), gleam@list:flat_map( _pipe@1, fun(_capture) -> find_all_direct_children(_capture, Selector@1) end ); {find_descendant, Parent@1, Selector@2} -> _pipe@2 = Root, _pipe@3 = find_all(_pipe@2, Parent@1), gleam@list:flat_map( _pipe@3, fun(_capture@1) -> find_all_descendants(_capture@1, Selector@2) end ) end. -file("src/agnostic/dev/query.gleam", 547). ?DOC( " Check if an element or any of its descendants match the given\n" " [`Selector`](#Selector).\n" ). -spec has(agnostic@vdom@vnode:element(any()), selector()) -> boolean(). has(Element, Selector) -> case find(Element, {find_element, Selector}) of {ok, _} -> true; {error, _} -> false end. -file("src/agnostic/dev/query.gleam", 792). -spec sort_selectors(list(selector())) -> list(selector()). sort_selectors(Selectors) -> gleam@list:sort( begin gleam@list:flat_map(Selectors, fun(Selector) -> case Selector of {all, Selectors@1} -> Selectors@1; _ -> [Selector] end end) end, fun(A, B) -> case {A, B} of {{all, _}, _} -> erlang:error(#{gleam_error => panic, message => <<"`All` selectors should be flattened"/utf8>>, file => <>, module => <<"agnostic/dev/query"/utf8>>, function => <<"sort_selectors"/utf8>>, line => 803}); {_, {all, _}} -> erlang:error(#{gleam_error => panic, message => <<"`All` selectors should be flattened"/utf8>>, file => <>, module => <<"agnostic/dev/query"/utf8>>, function => <<"sort_selectors"/utf8>>, line => 803}); {{type, _, _}, {type, _, _}} -> case gleam@string:compare( erlang:element(2, A), erlang:element(2, B) ) of eq -> gleam@string:compare( erlang:element(3, A), erlang:element(3, B) ); Order -> Order end; {{type, _, _}, _} -> lt; {_, {type, _, _}} -> gt; {{has_attribute, <<"id"/utf8>>, _}, {has_attribute, <<"id"/utf8>>, _}} -> gleam@string:compare( erlang:element(3, A), erlang:element(3, B) ); {{has_attribute, <<"id"/utf8>>, _}, _} -> lt; {_, {has_attribute, <<"id"/utf8>>, _}} -> gt; {{has_attribute, _, _}, {has_attribute, _, _}} -> case gleam@string:compare( erlang:element(2, A), erlang:element(2, B) ) of eq -> gleam@string:compare( erlang:element(3, A), erlang:element(3, B) ); Order@1 -> Order@1 end; {{has_attribute, _, _}, _} -> lt; {_, {has_attribute, _, _}} -> gt; {{has_style, _, _}, {has_style, _, _}} -> gleam@string:compare( erlang:element(2, A), erlang:element(2, B) ); {{has_style, _, _}, _} -> lt; {_, {has_style, _, _}} -> gt; {{has_class, _}, {has_class, _}} -> gleam@string:compare( erlang:element(2, A), erlang:element(2, B) ); {{has_class, _}, _} -> lt; {_, {has_class, _}} -> gt; {{has_text, _}, {has_text, _}} -> gleam@string:compare( erlang:element(2, A), erlang:element(2, B) ) end end ). -file("src/agnostic/dev/query.gleam", 764). -spec selector_to_readable_string(selector()) -> binary(). selector_to_readable_string(Selector) -> case Selector of {all, []} -> <<""/utf8>>; {type, <<""/utf8>>, <<""/utf8>>} -> <<""/utf8>>; {has_attribute, <<""/utf8>>, _} -> <<""/utf8>>; {has_class, <<""/utf8>>} -> <<""/utf8>>; {has_style, <<""/utf8>>, _} -> <<""/utf8>>; {has_style, _, <<""/utf8>>} -> <<""/utf8>>; {has_text, <<""/utf8>>} -> <<""/utf8>>; {all, Selectors} -> _pipe = Selectors, _pipe@1 = sort_selectors(_pipe), _pipe@2 = gleam@list:map(_pipe@1, fun selector_to_readable_string/1), erlang:list_to_binary(_pipe@2); {type, <<""/utf8>>, Tag} -> Tag; {type, Namespace, Tag@1} -> <<<>/binary, Tag@1/binary>>; {has_attribute, <<"id"/utf8>>, Value} -> <<"#"/utf8, Value/binary>>; {has_attribute, Name, <<""/utf8>>} -> <<<<"["/utf8, Name/binary>>/binary, "]"/utf8>>; {has_attribute, Name@1, Value@1} -> <<<<<<<<"["/utf8, Name@1/binary>>/binary, "=\""/utf8>>/binary, Value@1/binary>>/binary, "\"]"/utf8>>; {has_class, Name@2} -> <<"."/utf8, Name@2/binary>>; {has_style, Name@3, Value@2} -> <<<<<<<<"[style*=\""/utf8, Name@3/binary>>/binary, ":"/utf8>>/binary, Value@2/binary>>/binary, "\"]"/utf8>>; {has_text, Content} -> <<<<":contains(\""/utf8, (gleam@string:replace( Content, <<"\n"/utf8>>, <<"\\n"/utf8>> ))/binary>>/binary, "\")"/utf8>> end. -file("src/agnostic/dev/query.gleam", 750). ?DOC( " Print a `Query` as a human-readable string similar to a CSS selector. This\n" " function is primarily intended for debugging and testing purposes: for example,\n" " you might use this to include the selector in a snapshot test for easier\n" " review.\n" "\n" " > **Note**: while similar, this function is not guaranteed to produce a valid\n" " > CSS selector. Specifically, queries that use the [`text`](#text) selector\n" " > will not be valid CSS selectors as they use the `:contains` pseudo-class,\n" " > which is not part of the CSS spec!\n" ). -spec to_readable_string('query'()) -> binary(). to_readable_string(Query) -> case Query of {find_element, Selector} -> selector_to_readable_string(Selector); {find_child, Parent, Selector@1} -> <<<<(to_readable_string(Parent))/binary, " > "/utf8>>/binary, (selector_to_readable_string(Selector@1))/binary>>; {find_descendant, Parent@1, Selector@2} -> <<<<(to_readable_string(Parent@1))/binary, " "/utf8>>/binary, (selector_to_readable_string(Selector@2))/binary>> end.