Skip to content

Commit

Permalink
Add dom tools
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidAlphaFox committed Jan 4, 2020
1 parent 9b2b242 commit e31460d
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 6 deletions.
9 changes: 3 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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

Expand Down
99 changes: 99 additions & 0 deletions src/ai_dom_node.erl
Original file line number Diff line number Diff line change
@@ -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.
55 changes: 55 additions & 0 deletions src/ai_dom_render.erl
Original file line number Diff line number Diff line change
@@ -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,"</",TagBin/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,"</",TagBin/binary,">">>,
render(Rest,Stack,<<OldAcc/binary,Acc1/binary>>);
render([El|Rest],Stack,Acc) ->
case ai_dom_node:children(El) of
[]->
NodeBin = render(ai_dom_node:tag(El),El),
render(Rest,Stack,<<Acc/binary,NodeBin/binary>>);
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 -> <<Acc/binary,Attr/binary," ">>;
_->
AttrValue = ai_string:to_string(V),
<<Acc/binary,Attr/binary,"=\"",AttrValue/binary,"\" ">>
end
end,<<" ">>,maps:to_list(Attributes0)).

0 comments on commit e31460d

Please sign in to comment.