File Coverage

blib/lib/CSS/Object/Parser/Default.pm
Criterion Covered Total %
statement 58 60 96.6
branch 5 10 50.0
condition 6 12 50.0
subroutine 10 10 100.0
pod 2 2 100.0
total 81 94 86.1


line stmt bran cond sub pod time code
1             ##----------------------------------------------------------------------------
2             ## CSS Object Oriented - ~/lib/CSS/Object/Parser/Default.pm
3             ## Version v0.2.0
4             ## Copyright(c) 2020 DEGUEST Pte. Ltd.
5             ## Author: Jacques Deguest <jack@deguest.jp>
6             ## Created 2020/08/09
7             ## Modified 2024/09/05
8             ## All rights reserved
9             ##
10             ## This program is free software; you can redistribute it and/or modify it
11             ## under the same terms as Perl itself.
12             ##----------------------------------------------------------------------------
13             package CSS::Object::Parser::Default;
14             BEGIN
15             {
16 4     4   5498 use strict;
  4         14  
  4         179  
17 4     4   23 use warnings;
  4         9  
  4         248  
18 4     4   53 use Module::Generic;
  4         8  
  4         34  
19 4     4   1403 use parent qw( CSS::Object::Parser );
  4         11  
  4         27  
20 4     4   270 use CSS::Object::Rule;
  4         8  
  4         36  
21 4     4   1181 use CSS::Object::Selector;
  4         9  
  4         38  
22 4     4   985 use CSS::Object::Property;
  4         9  
  4         36  
23 4     4   7280 our $VERSION = 'v0.2.0';
24             };
25              
26             ## add a style to the style list
27             # From css spec at http://www.w3.org/TR/REC-CSS2/selector.html#q1
28             # * Matches any element. Universal selector
29             # E Matches any E element (i.e., an element of type E).
30             # E F Matches any F element that is a descendant of an E element.
31             # E > F Matches any F element that is a child of an element E.
32             # E:first-child Matches element E when E is the first child of its parent.
33             # E + F Matches any F element immediately preceded by a sibling element E.
34             # E[foo] Matches any E element with the "foo" attribute set (whatever the value).
35             # E[foo="warning"] Matches any E element whose "foo" attribute value is exactly equal to "warning".
36             # E[foo~="warning"] Matches any E element whose "foo" attribute value is a list of space-separated values,
37             # one of which is exactly equal to "warning".
38             # E[lang|="en"] Matches any E element whose "lang" attribute has a hyphen-separated list of values
39             # beginning (from the left) with "en".
40             # DIV.warning Language specific. (In HTML, the same as DIV[class~="warning"].)
41             # E#myid Matches any E element with ID equal to "myid". ID selectors
42             sub add_rule
43             {
44 18     18 1 109 my $self = shift( @_ );
45 18         103 my $style = shift( @_ );
46 18         57 my $contents = shift( @_ );
47 18   50     145 my $css = $self->css || return( $self->error( "Our css object is gone!" ) );
48            
49             # my $rule = CSS::Object::Rule->new(
50 18   50     691 my $rule = $css->new_rule(
51             # format => $self->format,
52             debug => $self->debug,
53             ) || return( $self->pass_error( CSS::Object::Rule->error ) );
54              
55             ## parse the selectors
56 18         15342 for my $name ( split( /[[:blank:]\h]*,[[:blank:]\h]*/, $style ) )
57             {
58             ## my $sel = CSS::Object::Selector->new({
59 33   50     12512 my $sel = $css->new_selector(
60             name => $name,
61             # format => $self->format,
62             debug => $self->debug,
63             ) || return( $self->error( "Unable to create a new CSS::Object::Selector objet: ", CSS::Object::Selector->error ) );
64 33 50       172 $rule->add_selector( $sel ) || return( $self->error( "Unable to add selector name '$name' to rule: ", $rule->error ) );
65             }
66              
67             ## parse the properties
68             ## Check possible comments and replace any ';' inside so they do not mess up this parsing here
69 18         15793 $contents =~ s{\/\*[[:blank:]\h]*(.*?)[[:blank:]\h]*\*\/}
70             {
71 2         9 my $cmt = $1;
72 2         17 $cmt =~ s/\;/__SEMI_COLON__/gs;
73 2         11 $cmt =~ s/\:/__COLON__/gs;
74 2         11 "/* $cmt */";
75             }sex;
76 18         151 foreach( grep{ /\S/ } split( /\;/, $contents ) )
  36         178  
77             {
78             ## Found one or more comments before the property
79 29         9190 while( s/^[[:blank:]\h]*\/\*[[:blank:]\h]*(.*?)[[:blank:]\h]*\*\///s )
80             {
81 2         12 my $txt = $1;
82 2         17 $txt =~ s/__SEMI_COLON__/\;/gs;
83 2         15 $txt =~ s/__COLON__/\:/gs;
84 2   50     23 my $cmt = $css->new_comment( [split( /\r?\n/, $txt )] ) || return( $self->error( "Unable to create a new CSS::Object::Comment object: ", CSS::Object::Comment->error ) );
85 2 50       2133 $rule->add_element( $cmt ) || return( $self->error( "Unable to add comment element to our rule: ", $rule->error ) );
86             }
87            
88 29 50       1651 unless( /^[[:blank:]\h]*(?<name>[\w\.\_\-]+)[[:blank:]\h]*:[[:blank:]\h]*(?<value>.*?)[[:blank:]\h]*$/ )
89             {
90 0         0 return( $self->error( "Invalid or unexpected property '$_' in style '$style'" ) );
91             }
92             ## Put back the colon we temporarily substituted to avoid confusion in the parser
93 29         364 $+{value} =~ s/__COLON__/\:/gs;
94             # my( $prop_name, $prop_val ) = @+{qw(name value)};
95             my $prop = CSS::Object::Property->new({
96             debug => $self->debug,
97             name => $+{name},
98             value => $+{value},
99             # format => $rule->format,
100 29   50     251 }) || return( $self->error( "Unable to create a new CSS::Object::Property object: ", CSS::Object::Property->error ) );
101 29 50       485 $rule->add_property( $prop ) || return( $self->error( "Unable to add property name '$+{name}' to rule: ", $rule->error ) );
102             }
103             # push( @{$self->{parent}->{styles}}, $rule );
104 18         16639 return( $rule );
105             }
106              
107             sub parse_string
108             {
109 5     5 1 9 my $self = shift( @_ );
110 5         12 my $string = shift( @_ );
111              
112 5         137 $string =~ s/\r\n|\r|\n/ /g;
113            
114 5         66 my $rules = Module::Generic::Array->new;
115             ## Split into styles
116 5         237 foreach( grep{ /\S/ } split( /(?<=\})/, $string ) )
  23         70  
117             {
118 18 50       353 unless( /^[[:blank:]\h]*([^{]+?)[[:blank:]\h]*\{(.*)\}[[:blank:]\h]*$/ )
119             {
120 0         0 return( $self->error( "Invalid or unexpected style data '$_'" ) );
121             }
122 18   50     134 my $rule = $self->add_rule( $1, $2 ) || return( $self->pass_error );
123 18         17230 $rules->push( $rule );
124             }
125 5         85 return( $rules );
126             }
127              
128             1;
129             # NOTE: POD
130             __END__
131              
132             =encoding utf-8
133              
134             =head1 NAME
135              
136             CSS::Object::Parser::Default - CSS Object Oriented Default Parser
137              
138             =head1 SYNOPSIS
139              
140             use CSS::Object;
141             my $css = CSS::Object->new(
142             parser => 'CSS::Object::Parser::Default',
143             format => $format_object,
144             debug => 3,
145             ) || die( CSS::Object->error );
146             $css->read( '/my/file.css' ) || die( $css->error );
147              
148             =head1 VERSION
149              
150             v0.2.0
151              
152             =head1 DESCRIPTION
153              
154             L<CSS::Object::Parser::Default> is a simple lightweight css parser.
155              
156             =head1 CONSTRUCTOR
157              
158             =head2 new
159              
160             To instantiate a new L<CSS::Object::Parser::Default> object, pass an hash reference of following parameters:
161              
162             =over 4
163              
164             =item I<debug>
165              
166             This is an integer. The bigger it is and the more verbose is the output.
167              
168             =back
169              
170             =head1 METHODS
171              
172             =head2 add_rule
173              
174             It takes 2 parameters: string of selectors and the rule content, i.e. inside the curly braces.
175              
176             It creates a new L<CSS::Object::Rule> object, adds to it a new L<CSS::Object::Selector> object for each selector found and also add a new L<CSS::Object::Property> object for each property found.
177              
178             It returns the rule object created.
179              
180             =head2 parse_string
181              
182             Provided with some css text data and this will parse it and return an array object of L<CSS::Object::Rule> objects. The array returned is an L<Module::Generic::Array> object.
183              
184             It does this by calling L</add_rule> on each rule found in the css data provided.
185              
186             Each L<CSS::Object::Rule> object containing one more more L<CSS::Object::Selector> objects and one or more L<CSS::Object::Property> objects.
187              
188             =head1 AUTHOR
189              
190             Jacques Deguest E<lt>F<jack@deguest.jp>E<gt>
191              
192             =head1 SEE ALSO
193              
194             L<CSS::Object>
195              
196             =head1 COPYRIGHT & LICENSE
197              
198             Copyright (c) 2020 DEGUEST Pte. Ltd.
199              
200             You can use, copy, modify and redistribute this package and associated
201             files under the same terms as Perl itself.
202              
203             =cut