| 846 | | |
| 847 | | package MT::Template::Node; |
| 848 | | |
| 849 | | use strict; |
| 850 | | use MT::Util qw( weaken ); |
| 851 | | |
| 852 | | sub setAttribute { |
| 853 | | my $node = shift; |
| 854 | | my ($attr, $val) = @_; |
| 855 | | if ($attr eq 'id') { |
| 856 | | # assign into ids |
| 857 | | my $tmpl = $node->template; |
| 858 | | my $ids = $tmpl->token_ids; |
| 859 | | my $old_id = $node->getAttribute("id"); |
| 860 | | if ($old_id && $ids) { |
| 861 | | delete $ids->{$old_id}; |
| 862 | | } |
| 863 | | } |
| 864 | | elsif ($attr eq 'class') { |
| 865 | | # assign into classes |
| 866 | | my $tmpl = $node->template; |
| 867 | | my $classes = $tmpl->token_classes; |
| 868 | | my $old_class = $node->getAttribute("class"); |
| 869 | | if ($old_class && $classes->{$old_class}) { |
| 870 | | @{$classes->{$old_class}} = grep { $_ != $node } |
| 871 | | @{$classes->{$old_class}}; |
| 872 | | } |
| 873 | | push @{$classes->{$val} ||= []}, $node; |
| 874 | | } |
| 875 | | ($node->[1] ||= {})->{$attr} = $val; |
| 876 | | } |
| 877 | | |
| 878 | | sub template { |
| 879 | | my $node = shift; |
| 880 | | return $node->[6]; |
| 881 | | } |
| 882 | | |
| 883 | | sub getAttribute { |
| 884 | | my $node = shift; |
| 885 | | my ($attr) = @_; |
| 886 | | ($node->[1] || {})->{$attr}; |
| 887 | | } |
| 888 | | |
| 889 | | # sub attributes { |
| 890 | | # my $node = shift; |
| 891 | | # return $node->[1] ||= {}; |
| 892 | | # } |
| 893 | | |
| 894 | | sub nextSibling { |
| 895 | | my $node = shift; |
| 896 | | my $parent = $node->parentNode->childNodes; |
| 897 | | my $max = (scalar @$parent) - 1; |
| 898 | | return undef unless $max; |
| 899 | | my $last = $parent->[0]; |
| 900 | | foreach my $n ($parent->[1..$max]) { |
| 901 | | return $n if $node == $last; |
| 902 | | $last = $n; |
| 903 | | } |
| 904 | | return $parent->[$max] if $node == $last; |
| 905 | | return undef; |
| 906 | | } |
| 907 | | |
| 908 | | sub lastChild { |
| 909 | | my $node = shift; |
| 910 | | my $children = $node->childNodes or return undef; |
| 911 | | @$children ? $children->[scalar @$children - 1] : undef; |
| 912 | | } |
| 913 | | |
| 914 | | sub firstChild { |
| 915 | | my $node = shift; |
| 916 | | my $children = $node->[2] or return undef; |
| 917 | | @$children ? $children->[0] : undef; |
| 918 | | } |
| 919 | | |
| 920 | | sub previousSibling { |
| 921 | | my $node = shift; |
| 922 | | my $parent = $node->parentNode->childNodes; |
| 923 | | my $last; |
| 924 | | foreach my $n (@$parent) { |
| 925 | | return $last if $node == $n; |
| 926 | | $last = $n; |
| 927 | | } |
| 928 | | return undef; |
| 929 | | } |
| 930 | | |
| 931 | | sub parentNode { |
| 932 | | my $node = shift; |
| 933 | | weaken($node->[5] = shift) if @_; |
| 934 | | $node->[5]; |
| 935 | | } |
| 936 | | |
| 937 | | sub childNodes { |
| 938 | | my $node = shift; |
| 939 | | $node->[2] = shift if @_; |
| 940 | | $node->[2]; |
| 941 | | } |
| 942 | | |
| 943 | | sub ownerDocument { #template |
| 944 | | my $node = shift; |
| 945 | | return $node->template; |
| 946 | | } |
| 947 | | |
| 948 | | sub hasChildNodes { |
| 949 | | my $node = shift; |
| 950 | | $node->[2] && (@{$node->[2]}) ? 1 : 0; |
| 951 | | } |
| 952 | | |
| 953 | | sub nodeType { |
| 954 | | my $node = shift; |
| 955 | | if ($node->[0] eq 'TEXT') { |
| 956 | | return NODE_TEXT(); |
| 957 | | } elsif (defined $node->[2]) { |
| 958 | | return NODE_BLOCK(); |
| 959 | | } else { |
| 960 | | return NODE_FUNCTION(); |
| 961 | | } |
| 962 | | } |
| 963 | | |
| 964 | | sub nodeName { |
| 965 | | my $node = shift; |
| 966 | | if ($node->[0] eq 'TEXT') { |
| 967 | | return undef; |
| 968 | | } |
| 969 | | # normalize: |
| 970 | | # MTEntry => mt:entry |
| 971 | | # MTAPP:WIDGET => mtapp:widget |
| 972 | | my $tag = lc $node->[0]; |
| 973 | | if (($tag !~ m/:/) && ($tag =~ m/^mt/)) { |
| 974 | | $tag =~ s/^mt/mt:/; |
| 975 | | } |
| 976 | | return $tag; |
| 977 | | } |
| 978 | | |
| 979 | | # Returns text of a text node; inner text for a block tag, or undef |
| 980 | | # for a function tag. |
| 981 | | sub nodeValue { |
| 982 | | my $node = shift; |
| 983 | | if ($node->[0] eq 'TEXT') { |
| 984 | | return $node->[1]; |
| 985 | | } else { |
| 986 | | if (defined $node->[3]) { |
| 987 | | return $node->[3]; |
| 988 | | } |
| 989 | | } |
| 990 | | return undef; |
| 991 | | } |
| 992 | | |
| 993 | | sub innerHTML { |
| 994 | | my $node = shift; |
| 995 | | if (@_) { |
| 996 | | my ($text) = @_; |
| 997 | | $node->[3] = $text; |
| 998 | | my $builder = new MT::Builder; |
| 999 | | my $ctx = MT::Template::Context->new; |
| 1000 | | $node->[2] = $builder->compile($ctx, $text); |
| 1001 | | my $tmpl = $node->ownerDocument; |
| 1002 | | if ($tmpl) { |
| 1003 | | $tmpl->reset_markers; |
| 1004 | | $tmpl->{reflow_flag} = 1; |
| 1005 | | } |
| 1006 | | } |
| 1007 | | return $node->[3]; |
| 1008 | | } |
| 1009 | | |
| 1010 | | # TBD: what about new nodes that are added with id elements? |
| 1011 | | sub appendChild { |
| 1012 | | my $node = shift; |
| 1013 | | my ($new_node) = @_; |
| 1014 | | my $nodes = $node->childNodes; |
| 1015 | | push @$nodes, $new_node; |
| 1016 | | my $tmpl = $node->ownerDocument; |
| 1017 | | if ($tmpl) { |
| 1018 | | $tmpl->{reflow_flag} = 1; |
| 1019 | | } |
| 1020 | | } |
| 1021 | | |
| 1022 | | sub removeChild { |
| 1023 | | my $node = shift; |
| 1024 | | } |
| 1025 | | |
| 1026 | | *inner_html = \&innerHTML; |
| 1027 | | *append_child = \&appendChild; |
| 1028 | | *insert_before = \&insertBefore; |
| 1029 | | *remove_child = \&removeChild; |