-module(sprocket@internal@render@html). -compile([no_auto_import, nowarn_unused_vars]). -export([renderer/0, preflight_renderer/2]). -export_type([inject_element_operation/0]). -type inject_element_operation() :: append | prepend. -spec text(binary()) -> gleam@string_builder:string_builder(). text(T) -> gleam@string_builder:from_string(T). -spec inject_element( sprocket@render:rendered_element(), binary(), sprocket@render:rendered_element(), inject_element_operation() ) -> sprocket@render:rendered_element(). inject_element(Root, Target_tag, Inject_element, Insert_op) -> sprocket@render:traverse(Root, fun(El) -> case El of {rendered_element, Tag, Key, Attrs, Children} when Tag =:= Target_tag -> {rendered_element, Target_tag, Key, Attrs, case Insert_op of append -> gleam@list:append(Children, [Inject_element]); prepend -> [Inject_element | Children] end}; _ -> El end end). -spec element( binary(), gleam@option:option(binary()), list(sprocket@render:rendered_attribute()), list(sprocket@render:rendered_element()) ) -> gleam@string_builder:string_builder(). element(Tag, Key, Attrs, Children) -> Rendered_attrs = begin _pipe = Attrs, gleam@list:fold( _pipe, gleam@string_builder:new(), fun(Acc, Attr) -> case Attr of {rendered_attribute, Name, Value} -> gleam@string_builder:append_builder( Acc, gleam@string_builder:from_strings( [<<" "/utf8>>, Name, <<"=\""/utf8>>, Value, <<"\""/utf8>>] ) ); {rendered_event_handler, Kind, Id} -> gleam@string_builder:append_builder( Acc, gleam@string_builder:from_strings( [<<" "/utf8>>, sprocket@internal@constants:constant( event_attr_prefix ), <<"-"/utf8>>, Kind, <<"=\""/utf8>>, Id, <<"\""/utf8>>] ) ) end end ) end, Rendered_attrs@1 = case Key of {some, K} -> gleam@string_builder:append_builder( Rendered_attrs, gleam@string_builder:from_strings( [<<" "/utf8>>, sprocket@internal@constants:constant(key_attr), <<"=\""/utf8>>, K, <<"\""/utf8>>] ) ); none -> Rendered_attrs end, Inner_html = begin _pipe@1 = Children, gleam@list:fold( _pipe@1, gleam@string_builder:new(), fun(Acc@1, Child) -> gleam@string_builder:append_builder(Acc@1, render(Child)) end ) end, gleam@string_builder:concat( [gleam@string_builder:from_string(<<"<"/utf8>>), gleam@string_builder:from_string(Tag), Rendered_attrs@1, gleam@string_builder:from_string(<<">"/utf8>>), Inner_html, gleam@string_builder:from_string(<<">), gleam@string_builder:from_string(Tag), gleam@string_builder:from_string(<<">"/utf8>>)] ). -spec render(sprocket@render:rendered_element()) -> gleam@string_builder:string_builder(). render(El) -> case El of {rendered_element, Tag, Key, Attrs, Children} -> element(Tag, Key, Attrs, Children); {rendered_component, _, _, _, _, Children@1} -> component(Children@1); {rendered_text, T} -> text(T) end. -spec renderer() -> sprocket@render:renderer(binary()). renderer() -> {renderer, fun(El) -> gleam@string_builder:to_string(render(El)) end}. -spec preflight_renderer( gleam@erlang@process:subject(sprocket@cassette:message()), sprocket@element:element() ) -> sprocket@render:renderer(binary()). preflight_renderer(Ca, View) -> _assert_subject = sprocket@internal@utils@uuid:v4(), {ok, Preflight_id} = case _assert_subject of {ok, _} -> _assert_subject; _assert_fail -> erlang:error(#{gleam_error => assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail, module => <<"sprocket/internal/render/html"/utf8>>, function => <<"preflight_renderer"/utf8>>, line => 25}) end, Csrf_token = sprocket@internal@csrf:generate(), sprocket@cassette:push_preflight( Ca, {preflight, Preflight_id, View, Csrf_token, os:system_time(millisecond)} ), Preflight_meta = begin _pipe = sprocket@html:meta( [sprocket@html@attributes:name( sprocket@internal@constants:constant(meta_preflight_id) ), sprocket@html@attributes:content(Preflight_id)] ), sprocket@render:render_element(_pipe) end, Csrf_meta = begin _pipe@1 = sprocket@html:meta( [sprocket@html@attributes:name( sprocket@internal@constants:constant(meta_crsf_token) ), sprocket@html@attributes:content(Csrf_token)] ), sprocket@render:render_element(_pipe@1) end, Sprocket_client = begin _pipe@2 = sprocket@html:script( [sprocket@html@attributes:src( sprocket@internal@constants:constant(client_script) )], none ), sprocket@render:render_element(_pipe@2) end, {renderer, fun(El) -> _pipe@3 = El, _pipe@4 = inject_element( _pipe@3, <<"head"/utf8>>, Preflight_meta, append ), _pipe@5 = inject_element( _pipe@4, <<"head"/utf8>>, Csrf_meta, append ), _pipe@6 = inject_element( _pipe@5, <<"body"/utf8>>, Sprocket_client, append ), _pipe@7 = render(_pipe@6), gleam@string_builder:to_string(_pipe@7) end}. -spec component(list(sprocket@render:rendered_element())) -> gleam@string_builder:string_builder(). component(Children) -> _pipe = Children, gleam@list:fold( _pipe, gleam@string_builder:new(), fun(Acc, Child) -> gleam@string_builder:append_builder(Acc, render(Child)) end ).