File Coverage

blib/lib/HTML/MobileJpCSS.pm
Criterion Covered Total %
statement 108 134 80.6
branch 42 64 65.6
condition 2 6 33.3
subroutine 13 14 92.8
pod 2 2 100.0
total 167 220 75.9


line stmt bran cond sub pod time code
1             package HTML::MobileJpCSS;
2 7     7   13661 use strict;
  7         17  
  7         255  
3 7     7   43 use warnings;
  7         18  
  7         253  
4 7     7   46 use Carp;
  7         50  
  7         590  
5 7     7   44 use File::Spec;
  7         16  
  7         191  
6 7     7   6405 use CSS::Tiny;
  7         3953  
  7         264  
7 7     7   938 use HTTP::MobileAgent;
  7         24809  
  7         28214  
8              
9             our $VERSION = '0.02';
10              
11             our %IstyleDoCoMo = (
12             '1' => '-wap-input-format:"*<ja:h>&;',
13             '2' => '-wap-input-format:"*<ja:hk>"',
14             '3' => '-wap-input-format:"*<ja:en>"',
15             '4' => '-wap-input-format:"*<ja:n>"',
16             );
17              
18             our $StyleMap = {
19             'hr' => {
20             'text-align' => {
21             'I' => 'float', 'S' => 'float',
22             },
23             'color' => {
24             'I' => 'border-color', 'S' => 'border-color',
25             },
26             },
27             'div' => {
28             'font-size' => [{
29             '10px' => { 'I' => 'font-size:xx-small', 'S' => 'font-size:small', },
30             '16px' => { 'I' => 'font-size:xx-large', 'S' => 'font-size:xx-large',},
31             }],
32             },
33             'span' => {
34             'font-size' => [{
35             '10px' => { 'I' => 'font-size:xx-small', 'S' => 'font-size:small', },
36             '16px' => { 'I' => 'font-size:xx-large', 'S' => 'font-size:xx-large', },
37             }],
38             },
39             'img' => {
40             'text-align' => [{
41             'left' => { 'I' => 'float:left', 'S' => 'float:left', },
42             'right' => { 'I' => 'float:right', 'S' => 'float:right', },
43             'center' => { 'I' => 'float:none', 'S' => 'float:none', },
44             }],
45             },
46             };
47              
48             sub new {
49 6     6 1 3924 my ($class, %args) = @_;
50 6         37 my $self = bless {%args}, $class;
51 6         37 $self->_init;
52 6         26 $self;
53             }
54              
55             sub apply {
56 6     6 1 15 my ($self, $content) = @_;
57 6 50       59 return $content if $self->{agent}->is_non_mobile;
58 6 50 66     73 return $content if $self->{agent}->is_ezweb && !(map { $self->{$_} } qw/inliner_ezweb css_file css/);
  3         18  
59              
60 6         176 $content =~ s/(?:\r\n|\n)/\n/g;
61              
62 6         17 my @css;
63 6         72 my @link = $content =~ //isg;
64 6         19 for (@link) {
65 4 50       38 if (/href="(.+?)"/) {
66 4         25 my $css = $self->_read_href($1);
67 4         16 push @css, $css;
68             }
69             }
70 6 100       68 $content =~ s/\s*//isg if @link;
71              
72 6 100       38 push @css, $self->_read($self->{css_file}) if $self->{css_file};
73 6 100       30 push @css, bless $self->{css}, 'CSS::Tiny' if $self->{css};
74              
75 6         17 my $style = {};
76 6         17 for my $css (@css) {
77 7         34 for (keys %$css) {
78 43 100       263 if (/^a:(?:link|focus|visited)$/) {
    50          
    100          
79 0         0 $style->{pseudo}->{$_} = $style->{pseudo}->{$_}
80 6 50       44 ? { %{$style->{pseudo}->{$_}}, %{$css->{$_}} }
  0         0  
81             : $css->{$_};
82             }
83             elsif (/^(\#(.+))/) {
84 0         0 $style->{id}->{$2} = $style->{id}->{$2}
85 0 0       0 ? { %{$style->{id}->{$2}}, %{$css->{$1}} }
  0         0  
86             : $css->{$1};
87             }
88             elsif (/^(\.(.+))/) {
89 1         4 $style->{class}->{$2} = $style->{class}->{$2}
90 31 100       172 ? { %{$style->{class}->{$2}}, %{$css->{$1}} }
  1         8  
91             : $css->{$1};
92             }
93             else {
94 0         0 $style->{tag}->{$_} = $style->{tag}->{$_}
95 6 50       41 ? { %{$style->{tag}->{$_}}, %{$css->{$_}} }
  0         0  
96             : $css->{$_};
97             }
98             }
99             }
100              
101             # pseudo
102 6 50       47 if ($style->{pseudo}) {
103 6         31 my $css = bless $style->{pseudo}, 'CSS::Tiny';
104 6 100       64 my $pseudo = $self->{agent}->is_docomo ? "write_string."]]>" : $css->write_string;
105 6         306 $content =~ s{(.*)}{$1}is;
106             }
107              
108             # tag
109 6         24 for my $tag (keys %{$style->{tag}}) {
  6         24  
110 6         29 my $props = $style->{tag}->{$tag};
111 6         230 my @node = $content =~ /<$tag[^<>]*?>/isg;
112 6         21 for my $node (@node) {
113 6         33 $content = $self->_replace_style($content, $node, $props);
114             }
115             }
116              
117             # id
118 6         17 for my $id (keys %{$style->{id}}) {
  6         47  
119 0         0 my $props = $style->{id}->{$id};
120 0         0 my @node = $content =~ /<[^<>]+?id="$id"[^<>]*?>/isg;
121 0         0 for my $node (@node) {
122 0         0 $content = $self->_replace_style($content, $node, $props);
123             }
124             }
125              
126             # class
127 6         15 for my $class (keys %{$style->{class}}) {
  6         29  
128 30         72 my $props = $style->{class}->{$class};
129 30         1850 my @node = $content =~ /<[^<>]+?\sclass="$class"[^<>]*?>/isg;
130 30         81 for my $node (@node) {
131 25         66 $content = $self->_replace_style($content, $node, $props);
132             }
133 30         2441 $content =~ s/<([^<>]+?)\sclass="$class"([^<>]*?)>/<$1$2>/isg;
134             }
135              
136             # istyle for DoCoMo
137 6 100       47 if ($self->{agent}->is_docomo) {
138 4         82 $content =~ s/(]*?)(istyle="(\d)")([^>]*?>)/$1style="$IstyleDoCoMo{$3}"$4/isg;
139 4         73 $content =~ s/(]*?)(istyle="(\d)")([^>]*?>)/$1style="$IstyleDoCoMo{$3}"$4/isg;
140             }
141 6         133 return $content;
142             }
143              
144             sub _init {
145 6     6   14 my $self = shift;
146 6 50       48 if ($self->{agent}) {
147 6 50       60 $self->{agent} = HTTP::MobileAgent->new($self->{agent}) unless (ref $self->{agent}) =~ /^HTTP::MobileAgent/;
148             }
149             else {
150 0         0 $self->{agent} = HTTP::MobileAgent->new();
151             }
152             }
153              
154             my %CSS;
155             sub _read_href {
156 4     4   15 my ($self, $href) = @_;
157 4 50       20 if ($href !~ m{^https?://}) {
158 4         11 $href =~ s/\?.+$//;
159 4         210 $href = File::Spec->catdir($self->{base_dir}, $href);
160 4         25 return $self->_read($href);
161             }
162             else {
163 0         0 return $self->_fetch($href);
164             }
165             }
166              
167             sub _read {
168 6     6   19 my ($self, $path) = @_;
169 6         14 my $css;
170 6 50       34 if ($css = $CSS{$path}) {
171 0 0       0 if ($css->{mtime} >= (stat($path))[9]) {
172 0         0 return $css->{style};
173             }
174             }
175 6 50       59 $css = CSS::Tiny->read($path)
176 0         0 or croak "Can't open '$path' by @{[ __PACKAGE__ ]}";
177 6 50       4639 return unless $css;
178 6         164 $CSS{$path} = {
179             style => $css,
180             mtime => (stat($path))[9],
181             };
182 6         29 return $css;
183             }
184              
185             sub _fetch {
186 0     0   0 my ($self, $url) = @_;
187 0         0 require LWP::UserAgent;
188 0   0     0 my $ua = $self->{_ua} || LWP::UserAgent->new(agent => __PACKAGE__ . "/$VERSION");
189 0         0 my $res = $ua->get($url);
190 0 0       0 if ($res->is_success) {
191 0 0       0 my $css = CSS::Tiny->read_string($res->content)
192 0         0 or croak "Can't parse '$url' by @{[ __PACKAGE__ ]}";
193 0         0 return $css;
194             }
195 0         0 croak "Can't fetch $url by @{[ __PACKAGE__ ]}";
  0         0  
196             }
197              
198             sub _replace_style {
199 31     31   72 my ($self, $content, $node, $props) = @_;
200 31         208 my ($tag) = $node =~ /^<([^\s]+).*?>/is;
201 31         54 my $replace = $node;
202 31         37 my $style;
203 31         94 for (keys %$props) {
204 41         119 $style .= $self->_filter($tag, $_, $props->{$_});
205             }
206 31 100       95 if ($node =~ /style=".+?"/) {
207 1         11 $replace =~ s/(style=".+?)"/$1$style"/is;
208             }
209             else {
210 30         371 $replace =~ s/<$tag/<$tag style="$style"/is;
211             }
212 31         234 $replace =~ s/[\n\s]+/ /g;
213 31         420 $content =~ s/$node/$replace/is;
214 31         163 return $content;
215             }
216              
217             sub _filter {
218 41     41   81 my ($self, $tagname, $property, $value) = @_;
219 41 100       157 return "$property:$value;" unless $StyleMap->{$tagname};
220 35 100       134 return "$property:$value;" unless $StyleMap->{$tagname}->{$property};
221 26         46 my $style = $StyleMap->{$tagname}->{$property};
222 26         101 my $carrier = $self->{agent}->carrier;
223 26         113 $carrier =~ s/^V$/S/;
224 26         55 $carrier =~ s/^H$/W/;
225 26 100       101 if (ref $style eq 'ARRAY') {
    50          
226 14         59 my $prop = $style->[0]->{$value}->{$carrier};
227 14 100       89 return "$prop;" if $prop;
228             }
229             elsif ($style) {
230 12         27 my $prop = $style->{$carrier};
231 12 100       79 return "$prop:$value;" if $prop;
232             }
233 5         21 return "$property:$value;";
234             }
235              
236             1;
237              
238             __END__