E[attr="foo"]
[catagits/HTML-Zoom.git] / lib / HTML / Zoom / SelectorParser.pm
CommitLineData
456a815d 1package HTML::Zoom::SelectorParser;
2
3use strict;
4use warnings FATAL => 'all';
6d0f20a6 5use base qw(HTML::Zoom::SubObject);
456a815d 6use Carp qw(confess);
7
8my $sel_char = '-\w_';
9my $sel_re = qr/([$sel_char]+)/;
10
11sub new { bless({}, shift) }
12
13sub _raw_parse_simple_selector {
14 for ($_[1]) { # same pos() as outside
15
16 # '*' - match anything
17
18 /\G\*/gc and
19 return sub { 1 };
20
3b8c9d7d 21 # 'el[attr="foo"]
22
23 /\G$sel_re\[$sel_re="$sel_re"\]/gc and
24 return do {
25 my $name = $1;
26 my $attr = $2;
27 my $val = $3;
28 sub {
29 $_[0]->{name} && $_[0]->{name} eq $name and
30 $_[0]->{attrs}{$attr} && $_[0]->{attrs}{$attr} eq $val
31 }
32 };
33
456a815d 34 # 'element' - match on tag name
35
36 /\G$sel_re/gc and
37 return do {
38 my $name = $1;
39 sub { $_[0]->{name} && $_[0]->{name} eq $name }
40 };
41
42 # '#id' - match on id attribute
43
44 /\G#$sel_re/gc and
45 return do {
46 my $id = $1;
47 sub { $_[0]->{attrs}{id} && $_[0]->{attrs}{id} eq $id }
48 };
49
50 # '.class1.class2' - match on intersection of classes
51
52 /\G((?:\.$sel_re)+)/gc and
53 return do {
54 my $cls = $1; $cls =~ s/^\.//;
55 my @cl = split(/\./, $cls);
56 sub {
57 $_[0]->{attrs}{class}
6d0f20a6 58 && !grep $_[0]->{attrs}{class} !~ /(^|\s+)$_($|\s+)/, @cl
456a815d 59 }
60 };
61
7871f2ff 62 # 'el.class1' - element + class
63
64 /\G$sel_re\.$sel_re/gc and
65 return do {
66 my $cls = $1;
67 my $name = $2;
68 sub {
69 $_[0]->{name} && $_[0]->{name} eq $name and
70 $_[0]->{attrs}{class} && $_[0]->{attrs}{class} eq $cls
71 }
72 };
73
32ea0d1e 74 # 'el#id' - element + id
75
76 /\G$sel_re#$sel_re/gc and
77 return do {
78 my $id = $1;
79 my $name = $2;
80 sub {
81 $_[0]->{name} && $_[0]->{name} eq $name and
82 $_[0]->{attrs}{id} && $_[0]->{attrs}{id} eq $id
83 }
84 };
85
456a815d 86 confess "Couldn't parse $_ as starting with simple selector";
87 }
88}
89
90sub parse_selector {
91 my $self = $_[0];
92 my $sel = $_[1]; # my pos() only please
93 die "No selector provided" unless $sel;
94 local *_;
95 for ($sel) {
96 my @sub;
97 PARSE: { do {
98 push(@sub, $self->_raw_parse_simple_selector($_));
99 last PARSE if (pos == length);
100 /\G\s*,\s*/gc or confess "Selectors not comma separated";
101 } until (pos == length) };
102 return $sub[0] if (@sub == 1);
103 return sub {
104 foreach my $inner (@sub) {
105 if (my $r = $inner->(@_)) { return $r }
106 }
107 };
108 }
7871f2ff 109}
110
456a815d 111
1121;