1 |
package Whatpm::CSS::MediaQueryParser; |
2 |
use strict; |
3 |
our $VERSION=do{my @r=(q$Revision: 1.1 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r}; |
4 |
|
5 |
use Whatpm::CSS::Tokenizer qw(:token); |
6 |
|
7 |
sub new ($) { |
8 |
my $self = bless { |
9 |
onerror => sub { }, |
10 |
level => { |
11 |
must => 'm', |
12 |
uncertain => 'u', |
13 |
}, |
14 |
}, shift; |
15 |
#$self->{href} = \(uri in which the MQ appears); |
16 |
return $self; |
17 |
} # new |
18 |
|
19 |
sub parse_char_string ($$) { |
20 |
my $self = $_[0]; |
21 |
|
22 |
my $s = $_[1]; |
23 |
pos ($s) = 0; |
24 |
|
25 |
my $tt = Whatpm::CSS::Tokenizer->new; |
26 |
$tt->{onerror} = $self->{onerror}; |
27 |
$tt->{line} = 1; |
28 |
$tt->{column} = 1; |
29 |
$tt->{get_char} = sub { |
30 |
if (pos $s < length $s) { |
31 |
$tt->{column} = 1 + pos $s; |
32 |
return ord substr $s, pos ($s)++, 1; |
33 |
} else { |
34 |
return -1; |
35 |
} |
36 |
}; # $tt->{get_char} |
37 |
$tt->init; |
38 |
|
39 |
my $t = $tt->get_next_token; |
40 |
$t = $tt->get_next_token while $t->{type} == S_TOKEN; |
41 |
|
42 |
my $r; |
43 |
($t, $r) = $self->_parse_mq_with_tokenizer ($t, $tt); |
44 |
return undef unless defined $r; |
45 |
|
46 |
if ($t->{type} != EOF_TOKEN) { |
47 |
$self->{onerror}->(type => 'mq syntax error', |
48 |
level => $self->{level}->{must}, |
49 |
uri => \$self->{href}, |
50 |
token => $t); |
51 |
return undef; |
52 |
} |
53 |
|
54 |
return $r; |
55 |
} # parse_char_string |
56 |
|
57 |
sub _parse_mq_with_tokenizer ($$$) { |
58 |
my ($self, $t, $tt) = @_; |
59 |
|
60 |
my $r = []; |
61 |
|
62 |
A: { |
63 |
## NOTE: Unknown media types are converted into 'unknown', since |
64 |
## Opera and WinIE do so and our implementation of the CSS |
65 |
## tokenizer currently normalizes numbers in NUMBER or DIMENSION tokens |
66 |
## so that the original representation cannot be preserved (e.g. '03d' |
67 |
## is covnerted to '3' with unit 'd'). |
68 |
|
69 |
if ($t->{type} == IDENT_TOKEN) { |
70 |
my $type = lc $t->{value}; ## TODO: case |
71 |
if ({ |
72 |
all => 1, braille => 1, embossed => 1, handheld => 1, print => 1, |
73 |
projection => 1, screen => 1, tty => 1, tv => 1, |
74 |
speech => 1, aural => 1, |
75 |
'atsc-tv' => 1, 'dde-tv' => 1, 'dvb-tv' => 1, |
76 |
dark => 1, emacs => 1, light => 1, xemacs => 1, |
77 |
}->{$type}) { |
78 |
push @$r, [['#type', $type]]; |
79 |
} else { |
80 |
push @$r, [['#type', 'unknown']]; |
81 |
$self->{onerror}->(type => 'unknown media type', |
82 |
level => $self->{level}->{uncertain}, |
83 |
uri => \$self->{href}, |
84 |
token => $t); |
85 |
} |
86 |
$t = $tt->get_next_token; |
87 |
} elsif ($t->{type} == NUMBER_TOKEN or $t->{type} == DIMENSION_TOKEN) { |
88 |
push @$r, [['#type', 'unknown']]; |
89 |
$self->{onerror}->(type => 'unknown media type', |
90 |
level => $self->{level}->{uncertain}, |
91 |
uri => \$self->{href}, |
92 |
token => $t); |
93 |
$t = $tt->get_next_token; |
94 |
} else { |
95 |
$self->{onerror}->(type => 'mq syntax error', |
96 |
level => $self->{level}->{must}, |
97 |
uri => \$self->{href}, |
98 |
token => $t); |
99 |
return ($t, undef); |
100 |
} |
101 |
|
102 |
$t = $tt->get_next_token while $t->{type} == S_TOKEN; |
103 |
if ($t->{type} == COMMA_TOKEN) { |
104 |
$t = $tt->get_next_token; |
105 |
$t = $tt->get_next_token while $t->{type} == S_TOKEN; |
106 |
redo A; |
107 |
} |
108 |
} # A |
109 |
|
110 |
return ($t, $r); |
111 |
} # _parse_mq_with_tokenizer |
112 |
|
113 |
1; |