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