Commit | Line | Data |
9d159224 |
1 | package HTML::Zoom::ActionBuilder; |
2 | |
3 | use strict; |
4 | use warnings FATAL => 'all'; |
5 | |
6 | use HTML::Zoom::Parser::BuiltIn; |
7 | use HTML::Zoom::EventFilter; |
8 | use Storable (); |
9 | use Carp qw(confess); |
10 | |
11 | sub build { |
12 | my ($tb, $name, @args) = @_; |
13 | confess "Don't know how to build ${name} in ${tb}" |
14 | unless $tb->can("build_${name}"); |
15 | |
16 | return $tb->${\"build_${name}"}(@args); |
17 | } |
18 | |
19 | sub build_add_attribute { |
20 | my ($tb, $name, $value) = @_; |
21 | sub { |
22 | my ($self, $evt) = @_; |
23 | delete @{$evt}{qw(raw raw_attrs)}; |
24 | if (defined $evt->{attrs}{$name}) { |
25 | $evt->{attrs}{$name} .= ' '.$value; |
26 | } else { |
27 | $evt->{attrs}{$name} = $value; |
28 | } |
29 | }; |
30 | } |
31 | |
32 | sub build_set_attribute { |
33 | my ($tb, $name, $value) = @_; |
34 | sub { |
35 | my ($self, $evt) = @_; |
36 | delete @{$evt}{qw(raw raw_attrs)}; |
37 | $evt->{attrs}{$name} = $value; |
38 | }; |
39 | } |
40 | |
41 | sub build_add_class { shift->build_add_attribute('class' => @_) } |
42 | sub build_set_class { shift->build_set_attribute('class' => @_) } |
43 | |
44 | sub build_replace_content { |
45 | my ($tb, $text) = @_; |
46 | my $raw = HTML::Zoom::Parser::BuiltIn::_simple_escape($text); |
47 | $tb->build_replace_content_raw($raw); |
48 | } |
49 | |
50 | sub build_replace_content_raw { |
51 | my ($tb, $raw) = @_; |
52 | sub { |
53 | my ($self, $evt) = @_; |
54 | my $in_place_close = $evt->{is_in_place_close}; |
55 | if ($in_place_close) { |
56 | delete $evt->{raw}; |
57 | $evt->{is_in_place_close} = 0; |
58 | } |
59 | $self->until_close_do_last( |
60 | undef, |
61 | sub { |
62 | delete $_[1]->{raw} if $in_place_close; |
63 | $_[0]->next({ type => 'TEXT', raw => $raw }) |
64 | } |
65 | ) |
66 | }; |
67 | } |
68 | |
69 | sub build_repeat { |
70 | my ($tb, $args) = @_; |
71 | my $data = $args->{data}; |
72 | my $do_repeat = sub { |
73 | my ($self, $to_repeat) = @_; |
74 | my $stole_last; |
75 | if ($to_repeat->[-1]->{type} eq 'TEXT') { |
76 | if ($to_repeat->[-1]->{raw} =~ s/(\s+)$//) { |
77 | $stole_last = $1; |
78 | } |
79 | } |
80 | foreach my $d (@$data) { |
81 | my @pairs = map { |
82 | HTML::Zoom::EventFilter->build_selector_pair($_, $d->{$_}) |
83 | } sort keys %$d; |
84 | my $sel = HTML::Zoom::EventFilter->selector_handler(\@pairs); |
85 | $sel->set_next($self->get_next); |
86 | $sel->call($_) for @$to_repeat; |
87 | } |
88 | if (defined $stole_last) { |
89 | $self->next({ type => 'TEXT', raw => $stole_last }); |
90 | } |
91 | }; |
92 | sub { |
93 | my ($self, $evt) = @_; |
94 | return if $evt->{is_in_place_close}; # no content to repeat |
95 | my @repeat; |
96 | $self->until_close_do_last( |
97 | sub { push(@repeat, $_[1]) }, |
98 | sub { $_[0]->$do_repeat(\@repeat) }, |
99 | ); |
100 | }; |
101 | } |
102 | |
103 | 1; |