From e31460ddaac7e799b3e4bc7c16d4f608560014d0 Mon Sep 17 00:00:00 2001 From: "David.Gao" Date: Sat, 4 Jan 2020 13:50:09 +0800 Subject: [PATCH] Add dom tools --- README.md | 9 ++-- src/ai_dom_node.erl | 99 +++++++++++++++++++++++++++++++++++++++++++ src/ai_dom_render.erl | 55 ++++++++++++++++++++++++ 3 files changed, 157 insertions(+), 6 deletions(-) create mode 100644 src/ai_dom_node.erl create mode 100644 src/ai_dom_render.erl diff --git a/README.md b/README.md index 6fa63f4..5910c03 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # aihtml -## Erlang Mustache Template Compiler +A simple html render libary more than Mustache Template Complier + +## Erlang Mustache Template Mustache is a framework-agnostic templating system that enforces separation of view logic from the template file. Indeed, it is not even possible to embed logic in the template. This @@ -15,11 +17,6 @@ are called and provide the data for the template tags. A context is an Erlang dict that contains the current context from which tags can pull data. A few examples will clarify how these items interact. -NOTE: This is alpha software. Do not use it in production without extensive -testing. The API may change at any time. It still lacks some of the features -of Mustache for Ruby and the performance (even with compiled templates) is not -yet where I'd like it to be. - ## Installation diff --git a/src/ai_dom_node.erl b/src/ai_dom_node.erl new file mode 100644 index 0000000..8a82368 --- /dev/null +++ b/src/ai_dom_node.erl @@ -0,0 +1,99 @@ +-module(ai_dom_node). + +-export([new/0,new/1,new/2]). +-export([insert_child/3,append_child/2,remove_child/2, + append_children/2,remove_children/1,children/1]). +-export([insert_attribute/3,insert_attributes/2,remove_attribute/2, + attribute/2,remove_attributes/1, attributes/1]). +-export([set_value/2,set_id/2,set_tag/2,value/1,id/1,tag/1]). +-export([set_opening/2,opening/1,set_slash/2,slash/1]). + +-record(ai_dom_node,{ + id :: binary(), %% 节点ID + tag :: atom(), %% 节点名字 + attributes :: maps:maps(), %% 节点属性 + children :: [], %% 子节点 + value :: term(), %% 节点值 + opening :: boolean(), %% 开放标签 + slash :: boolean() %% 开放标签后面是否有slash + }). + +-opaque ai_dom_node() :: #ai_dom_node{}. +-export_type([ai_dom_node/0]). + +new()-> new(undefined,undefined). +new(Tag)-> new(undefined,Tag). +new(ID,Tag)-> + #ai_dom_node{ + id = ID,tag = Tag, + attributes = maps:new(),children = [], + value = undefined, + opening = false, + slash = false + }. +insert_child(Index,Child,Node)-> + case Node#ai_dom_node.children of + [] when Index == 0 -> + Node#ai_dom_node{children = [Child]}; + [] -> + throw({error,out_of_range}); + Children -> + {_N,Children0} = + lists:foldl(fun(C,{N,Acc})-> + if N == Index -> {N + 2,[C,Child|Acc]}; + true -> {N+1,[C|Acc]} + end + end,{0,[]},Children), + Node#ai_dom_node{children = lists:reverse(Children0)} + end. + +append_child(Child,Node)-> + Node#ai_dom_node{ + children = lists:append(Node#ai_dom_node.children,[Child]) + }. +remove_child(Index,Node)-> + {CHead,[_I|CTail]} = lists:split(Index,Node#ai_dom_node.children), + Node#ai_dom_node{ + children = lists:append(CHead,CTail) + }. +remove_children(Node)-> Node#ai_dom_node{children = []}. +append_children(Children,Node)-> + Node#ai_dom_node{ + children = lists:append(Node#ai_dom_node.children,Children) + }. +children(Node)-> Node#ai_dom_node.children. + +insert_attribute(Name,Value,Node)-> + Node#ai_dom_node{ + attributes = maps:put(Name,Value,Node#ai_dom_node.attributes) + }. +remove_attribute(Name,Node)-> + Node#ai_dom_node{ + attributes = maps:remove(Name,Node#ai_dom_node.attributes) + }. +insert_attributes(Attributes,Node)-> + Node#ai_dom_node{ + attributes = maps:merge(Node#ai_dom_node.attributes,Attributes) + }. +remove_attributes(Node)-> + Node#ai_dom_node{ + attributes = maps:new() + }. +attribute(Name,Node)-> + Attributes = Node#ai_dom_node.attributes, + maps:get(Name,Attributes,undefined). + +attributes(Node)->Node#ai_dom_node.attributes. + + +set_value(Value,Node)-> Node#ai_dom_node{value = Value}. +set_tag(Tag,Node)->Node#ai_dom_node{tag = Tag}. +set_id(ID,Node)-> Node#ai_dom_node{id = ID}. +value(Node)-> Node#ai_dom_node.value. +tag(Node)-> Node#ai_dom_node.tag. +id(Node)-> Node#ai_dom_node.id. + +set_opening(Value,Node)-> Node#ai_dom_node{opening = Value}. +set_slash(Value,Node)-> Node#ai_dom_node{slash = Value}. +opening(Node)-> Node#ai_dom_node.opening. +slash(Node)-> Node#ai_dom_node.slash. \ No newline at end of file diff --git a/src/ai_dom_render.erl b/src/ai_dom_render.erl new file mode 100644 index 0000000..da89f6e --- /dev/null +++ b/src/ai_dom_render.erl @@ -0,0 +1,55 @@ +-module(ai_dom_render). +-export([render/1]). + +render(Node)-> render([Node],[],<<>>). +render(undefined,Node)-> ai_string:to_string(ai_dom_node:value(Node)); +render(Tag,Node) -> + TagBin = ai_string:to_string(Tag), + Attributes = attributes(Node), + case ai_dom_node:opening(Tag) of + false -> + Value = + case ai_dom_node:value(Node) of + undefined -> <<"">>; + NV -> ai_string:to_string(NV) + end, + <<"<",TagBin/binary,Attributes/binary,">",Value/binary,"">>; + true -> + EndTag = + case ai_dom_node:slash(Node) of + true -> <<" />">>; + false -> <<" >">> + end, + <<"<",TagBin/binary,Attributes/binary,EndTag/binary>> + end. +render([],[],Acc)-> Acc; +render([],[{Parent,Rest,OldAcc}|Stack],Acc)-> + TagBin = ai_string:to_string(ai_dom_node:tag(Parent)), + Attributes = attributes(Parent), + Acc1 = <<"<",TagBin/binary,Attributes/binary,">",Acc/binary,"">>, + render(Rest,Stack,<>); +render([El|Rest],Stack,Acc) -> + case ai_dom_node:children(El) of + []-> + NodeBin = render(ai_dom_node:tag(El),El), + render(Rest,Stack,<>); + Children -> render(Children,[{El,Rest,Acc}|Stack],<<>>) + end. + +attributes(Node)-> + Attributes = ai_dom_node:attributes(Node), + Attributes0 = + case ai_dom_node:id(Node) of + undefined -> Attributes; + ID -> maps:put(id,ID,Attributes) + end, + lists:foldl(fun({K,V},Acc)-> + Attr = ai_string:to_string(K), + case V of + ture -> <>; + _-> + AttrValue = ai_string:to_string(V), + <> + end + end,<<" ">>,maps:to_list(Attributes0)). +