minimal sdl.cgi script plus deps
[sdlgit/SDL-Site.git] / code / HTML / Zoom / EventFilter.pm
CommitLineData
9d159224 1package HTML::Zoom::EventFilter;
2
3use strict;
4use warnings FATAL => 'all';
5use Carp qw(confess);
6
7sub from_code {
8 my ($class, $code) = @_;
9 confess "from_code is a class method" if ref $class;
10 bless({ code => $code }, $class);
11}
12
13sub next {
14 my $n = shift->{next} or return;
15 $n->${\$n->{code}}(@_);
16}
17
18sub call {
19 $_[0]->{code}->(@_);
20}
21
22sub set_next {
23 $_[0]->{next} = $_[1];
24 $_[0];
25}
26
27sub get_next { $_[0]->{next} }
28
29sub push_next {
30 my ($self, $code) = @_;
31 $self->{next} = bless(
32 { code => $code, next => $self->{next} }
33 );
34}
35
36sub push_last {
37 my ($self, $code) = @_;
38 my $target = $self;
39 while ($target->{next} && $target->{next}{next}) {
40 $target = $target->{next}
41 }
42 $target->push_next($code);
43}
44
45sub pop {
46 my ($self, $to) = @_;
47 die "$self doesn't have a next (->pop($to))"
48 unless $self->{next};
49 my $target = $self;
50 until ($target->{next} eq $to) {
51 $target = $target->{next} || die "Didn't find $to as next of $self";
52 }
53 $target->{next} = $to->{next};
54 $_[0];
55}
56
57sub until_close_do_next { shift->until_close_do(next => @_) }
58sub until_close_do_last { shift->until_close_do(last => @_) }
59
60sub until_close_do {
61 my ($self, $direction, $do, $before_close, $after_close) = @_;
62 my %depth = (OPEN => 1, CLOSE => -1, TEXT => 0);
63 my $count = 1;
64 my $outer = $self;
65 $self->${\"push_${direction}"}(
66 sub {
67 my ($self, $evt) = @_;
68 $count += $depth{$evt->{type}};
69 if ($count) {
70 $do->(@_, $count) if $do;
71 return;
72 }
73 $before_close->($self, $evt) if $before_close;
74 $outer->pop($self)->next($evt);
75 $after_close->($outer, $evt) if $after_close;
76 }
77 )
78}
79
80sub standard_emitter {
81 my ($class, $out) = @_;
82 confess "standard_emitter is a class method" if ref $class;
83 $class->from_code(sub {
84 my ($self, $evt) = @_;
85 return $out->print($evt->{raw}) if defined $evt->{raw};
86 if ($evt->{type} eq 'OPEN') {
87 $out->print(
88 '<'
89 .$evt->{name}
90 .(defined $evt->{raw_attrs}
91 ? $evt->{raw_attrs}
92 : do {
93 my @names = @{$evt->{attr_names}};
94 @names
95 ? join(' ', '', map qq{${_}="${\$evt->{attrs}{$_}}"}, @names)
96 : ''
97 }
98 )
99 .($evt->{is_in_place_close} ? ' /' : '')
100 .'>'
101 );
102 } elsif ($evt->{type} eq 'CLOSE') {
103 $out->print('</'.$evt->{name}.'>');
104 } else {
105 confess "No raw value in event and no special handling for type ".$evt->{type};
106 }
107 });
108}
109
110sub selector_handler {
111 my ($class, $pairs) = @_;
112 confess "selector_handler is a class method" if ref $class;
113 $class->from_code(sub {
114 my ($self, $evt) = @_;
115 my $next = $self->get_next;
116 if ($evt->{type} eq 'OPEN') {
117 foreach my $p (@$pairs) {
118 $p->[1]->($self, $evt) if $p->[0]->($evt);
119 }
120 }
121 $next->call($evt);
122 });
123}
124
125sub build_selector_pair {
126 my ($class, $sel_spec, $action_spec) = @_;
127 my $selector = HTML::Zoom::SelectorParser->parse_selector($sel_spec);
128 my $action;
129 if (ref($action_spec) eq 'HASH') {
130 confess "hash spec must be single key"
131 unless keys(%$action_spec) == 1;
132 my ($key) = keys (%$action_spec);
133 $key =~ s/^-//;
134 $action = HTML::Zoom::ActionBuilder->build($key, values %$action_spec);
135 }
136 [ $selector, $action ];
137}
138
1391;