CSS-DOM

 view release on metacpan or  search on metacpan

lib/CSS/DOM.pm  view on Meta::CPAN


=item CSS::DOM::parse

See L</CONSTRUCTORS>, above.

=item CSS::DOM::compute_style( %options )

B<Warning:> This is still highly experimental and crawling with bugs.

This computes the style for a given HTML element. It does not yet calculate
actual measurements (e.g., converting percentages to pixels), but simply
applies the cascading rules and selectors. Pseudo-classes are
not yet supported (but pseudo-elements are).

The precedence rules for normal vs important declarations in the CSS 2 
specification are used. (CSS 2.1 is unclear.) The precedence is as follows,
from lowest to highest:

 user agent normal declarations
 user normal declarations
 author normal     "

lib/CSS/DOM/Parser.pm  view on Meta::CPAN

my $_nl        = qr/\r\n?|[\n\f]/;
my $_invalid_qq = qr/"[^\n\r\f\\"]*(?:(?:\\$_nl|$_escape)[^\n\r\f\\"]*)*/;
my $_invalid_q  = qr/'[^\n\r\f\\']*(?:(?:\\$_nl|$_escape)[^\n\r\f\\']*)*/;

my $ident = qr/-?$_id_start$_id_cont*/;
my $at    = qr/\@$ident/;
my $str   = qr/$_invalid_qq(?:"|\z)|$_invalid_q(?:'|\z)/;
my $invalid = qr/$_invalid_qq|$_invalid_q/;
my $hash     = qr/#$_id_cont+/;
my $num      = qr/(?=\.?[0-9])[0-9]*(?:\.[0-9]*)?/;
my $percent  = qr/$num%/;
my $dim      = qr/$num$ident/;
my $url      = qr/url\($_optspace(?:
		$str
	  	  |
		[^\0- "'()\\\x7f]*(?:$_escape[^\0- "'()\\\x7f]*)*
	)$_optspace(?:\)|\z)/x;
my $uni_range = qr/U\+[0-9A-F?]{1,6}(?:-[0-9a-f]{1,6})?/i;
my $space     = qr/(?:[ \t\r\n\f]+|\/\*.*?(?:\*\/|\z))[ \t\r\n\f]*
                   (?:\/\*.*?(?:\*\/|\z)[ \t\r\n\f]*)*/xs;
my $function  = qr/$ident\(/;

# Literal tokens are as follows:
#  <!-- --> ; { } ( ) [ ] ~= |= , :

# The order of some tokens is important. $url, $uni_range and $function
# have to come before $ident. $url has to come before $function. $percent
# and $dim have to come before $num. 
$token_re = qr/\G(?:
        ($url)|($uni_range)|($function)|($ident)|($at)|($str)|($invalid)|
        ($hash)|($percent)|($dim)|($num)|(<!--|-->)|(;)|(\{)|(})|(\()|(\))
       |(\[)|(])|($space)|(~=)|(\|=)|(,)|(:)|(.)
)/xs;

} # end of tokeniser regexps

# tokenise returns a string of token types in addition to the array of
# tokens so that we can apply grammar rules using regexps. The types are
# as follows:
#  u  url
#  U  unicode range
#  f  function
#  i  identifier
#  @  at keyword
#  '  string
#  "  invalid string (unterminated)
#  #  hash
#  %  percentage
#  D  dimension
#  1  number  (not 0, because we want it true)
#  <  html comment delimiter
#  s  space/comments
#  ~  ~=
#  |  |=
#  d  delimiter (miscellaneous character)
# The characters ;{}()[],: represent themselves. The comma and colon are
# actually delimiters according to the CSS 2.1 spec, but it’s more conveni-
# ent to have them as their own tokens.

lib/CSS/DOM/PropertyParser.pm  view on Meta::CPAN

      "(?:" . cap_start . "(i)(?{ $type_is_ CSS_IDENT })" . cap_end . ")"
   : $3 eq 'integer'    ?
      '(?:' . cap_start . '(1(?(?{index$$tokens[pos()-1],".",==-1})|(?!)))'
      . "(?{ $type_is_ CSS_NUMBER })" . cap_end . ")"
   : $3 eq 'length'     ?
      "(?:" . cap_start . "($sign\[D1])" . '(?(?{
        $$alt_types[pos()-1]eq"l"||$$prepped[pos()-1]eq 0
       })|(?!))' . $type_is_dim_or_number . cap_end . ")"
   : $3 eq 'number'     ?
      "(?:" . cap_start . "(1)(?{ $type_is_ CSS_NUMBER })" . cap_end . ")"
   : $3 eq 'percentage' ?
       "(?:" . cap_start
        . "($sign%)(?{ $type_is_ CSS_PERCENTAGE })"
      . cap_end . ")"
   : $3 eq 'shape'      ?
      q*(?x:* . cap_start . q*(f
          (?(?{$$prepped[pos()-1] eq "rect("})
            (?:
             (?:
              (?:d(?(?{$$alt_types[pos()-1]eq"+"})|(?!)))?[D1](?(?{
               $$alt_types[pos()-1]eq"l"||$$prepped[pos()-1] eq 0

lib/CSS/DOM/PropertyParser.pm  view on Meta::CPAN

               predefined colour or system colour names, or a #
               followed by 3 or 6 hex digits, or the 'rgb(...)'
               format (rgba is supported, too)
 <counter>     counter(...)
 <frequency>   A unit of Hz or kHz
 <identifier>  An identifier token
 <integer>     An integer (really?!)
 <length>      Number followed by a length unit (em, ex, px, in, cm,
               mm, pt, pc)
 <number>      A number token
 <percentage>  Number followed by %
 <shape>       rect(...)
 <string>      A string token
 <str/words>   A sequence of identifiers or a single string (e.g., a
               font name)
 <time>        A unit of seconds or milliseconds
 <url>         A URL token

The format for a shorthand property can contain the name of a sub-property
in single ASCII quotes.

lib/CSS/DOM/PropertyParser.pm  view on Meta::CPAN

     inherit => 0,
    },

   'background-image' => {
     format => '<url> | none',
     default => 'none',
     inherit => 0,
    },
    
   'background-position' => {
     format => '[<percentage>|<length>|left|right]
                 [<percentage>|<length>|top|center|bottom]? |
                [top|bottom] [left|center|right]? |
                center [<percentage>|<length>|left|right|top|bottom|
                        center]?',
     default => '0% 0%',
     inherit => 0,
    },
    
   'background-repeat' => {
     format => 'repeat | repeat-x | repeat-y | no-repeat',
     default => 'repeat',
     inherit => 0,
    },

lib/CSS/DOM/PropertyParser.pm  view on Meta::CPAN

           $temp eq $p->{"border-$side-$_"} or return "";
         }
         length $temp and $ret .= "$temp ";
       }
       chop $ret;
       $ret
     },
    },
    
    bottom => {
     format => '<length>|<percentage>|auto',
     default => 'auto',
     inherit => 0,
    },
    
   'caption-side' => {
     format => 'top|bottom',
     default => 'top',
     inherit => 1,
    },
    

lib/CSS/DOM/PropertyParser.pm  view on Meta::CPAN

                 <str/words>)
                [,(serif|sans-serif|cursive|fantasy|monospace|
                   <str/words>)]*',
     default => 'Times, serif',
     inherit => 1,
     list => 1,
    },
    
   'font-size' => {
     format => 'xx-small|x-small|small|medium|large|x-large|xx-large|
                larger|smaller|<length>|<percentage>',
     default => 'medium',
     inherit => 1,
    },
    
   'font-style' => {
     format => 'normal|italic|oblique',
     default => 'normal',
     inherit => 1,
    },
    

lib/CSS/DOM/PropertyParser.pm  view on Meta::CPAN

       $ret .= length $p->{'font-size'}
               ? $p->{'font-size'}
               : 'medium';
       $ret .= "/$p->{'line-height'}" if length $p->{'line-height'};
       $ret .= " " . ($p->{'font-family'} || "Times, serif");
       $ret
     },
    },
    
    height => {
     format => '<length>|<percentage>|auto',
     default => 'auto',
     inherit => 0,
    },
    
    left => {
     format => '<length>|<percentage>|auto',
     default => 'auto',
     inherit => 0,
    },
    
   'letter-spacing' => { # aka tracking
     format => 'normal|<length>',
     default => 'normal',
     inherit => 1,
    },
    
   'line-height' => { # aka leading
     format => 'normal|<number>|<length>|<percentage>',
     default => "normal",
     inherit => 1,
    },
    
   'list-style-image' => {
     format => '<url>|none',
     default => 'none',
     inherit => 1,
    },
    

lib/CSS/DOM/PropertyParser.pm  view on Meta::CPAN

       for(qw/ type position image /) {
         $p->{"list-style-$_"}
           and $ret .= $p->{"list-style-$_"}." ";
       }
       chop $ret;
       $ret || 'disc'
     },
    },
    
   'margin-right' => {
     format => '<length>|<percentage>|auto',
     default => '0',
     inherit => 0,
    },
   'margin-left' => {
     format => '<length>|<percentage>|auto',
     default => '0',
     inherit => 0,
    },
   'margin-top' => {
     format => '<length>|<percentage>|auto',
     default => '0',
     inherit => 0,
    },
   'margin-bottom' => {
     format => '<length>|<percentage>|auto',
     default => '0',
     inherit => 0,
    },
    
    margin => {
     format => "(<length>|<percentage>|auto)
                [ (<length>|<percentage>|auto)
                  [ (<length>|<percentage>|auto)
                    (<length>|<percentage>|auto)?
                  ]?
                ]?",
     properties => {
      'margin-top' => [1],
      'margin-right' => [2,1],
      'margin-bottom' => [3,1],
      'margin-left' => [4,2,1],
     },
     serialise => sub {
       my $p = shift;
       my @vals = map $p->{"margin-$_"},
                      qw/top right bottom left/;
       $vals[3] eq $vals[1] and pop @vals,
       $vals[2] eq $vals[0] and pop @vals,
       $vals[1] eq $vals[0] and pop @vals;
       return join " ", map $_ || 0, @vals;
     },
    },
    
   'max-height' => {
     format => '<length>|<percentage>|none',
     default => 'none',
     inherit => 0,
    },
   'max-width' => {
     format => '<length>|<percentage>|none',
     default => 'none',
     inherit => 0,
    },
   'min-height' => {
     format => '<length>|<percentage>|none',
     default => 'none',
     inherit => 0,
    },
   'min-width' => {
     format => '<length>|<percentage>|none',
     default => 'none',
     inherit => 0,
    },
    
    orphans => {
     format => '<integer>',
     default => 2,
     inherit => 1,
    },
    

lib/CSS/DOM/PropertyParser.pm  view on Meta::CPAN

     },
    },
    
    overflow => {
     format => 'visible|hidden|scroll|auto',
     default => 'visible',
     inherit => 0,
    },
    
   'padding-top' => {
     format => '<length>|<percentage>',
     default => 0,
     inherit => 0,
    },
   'padding-right' => {
     format => '<length>|<percentage>',
     default => 0,
     inherit => 0,
    },
   'padding-bottom' => {
     format => '<length>|<percentage>',
     default => 0,
     inherit => 0,
    },
   'padding-left' => {
     format => '<length>|<percentage>',
     default => 0,
     inherit => 0,
    },
    
    padding => {
     format => "(<length>|<percentage>)
                [ (<length>|<percentage>)
                  [ (<length>|<percentage>)
                    (<length>|<percentage>)?
                  ]?
                ]?",
     properties => {
      'padding-top' => [1],
      'padding-right' => [2,1],
      'padding-bottom' => [3,1],
      'padding-left' => [4,2,1],
     },
     serialise => sub {
       my $p = shift;

lib/CSS/DOM/PropertyParser.pm  view on Meta::CPAN

     inherit => 0,
    },
    
   'page-break-inside' => {
     format => 'avoid|auto',
     default => 'auto',
     inherit => 1,
    },
    
   'pause-after' => {
      format => '<time>|<percentage>',
      default => 0,
      inherit => 0,
    },
   'pause-before' => {
      format => '<time>|<percentage>',
      default => 0,
      inherit => 0,
    },
    
    pause => {
     format => '(<time>|<percentage>)(<time>|<percentage>)?',
     properties => {
      'pause-before' => [1],
      'pause-after' => [2,1],
     }
    },
    
   'pitch-range' => {
     format => '<number>',
     default => 50,
     inherit => 1,

lib/CSS/DOM/PropertyParser.pm  view on Meta::CPAN

     list => 1,
    },
    
    richness => {
     format => '<number>',
     default => 50,
     inherit => 1,
    },
    
    right => {
     format => '<length>|<percentage>|auto',
     default => 'auto',
     inherit => 0,
    },
    
   'speak-header' => {
     format => 'once|always',
     default => 'once',
     inherit => 1,
    },
    

lib/CSS/DOM/PropertyParser.pm  view on Meta::CPAN

     inherit => 1,
    },
    
   'text-decoration' => {
     format => 'none | underline||overline||line-through||blink ',
     default => 'none',
     inherit => 0,
    },
    
   'text-indent' => {
     format => '<length>|<percentage>',
     default => 0,
     inherit => 1,
    },
    
   'text-transform' => {
     format => 'capitalize|uppercase|lowercase|none',
     default => 'none',
     inherit => 1,
    },
    
    top => {
     format => '<length>|<percentage>|auto',
     default => 'auto',
     inherit => 0,
    },
    
   'unicode-bidi' => {
     format => 'normal|embed|bidi-override',
     default => 'normal',
     inherit => 0,
    },
    
   'vertical-align' => {
     format => 'baseline|sub|super|top|text-top|middle|bottom|
                text-bottom|<percentage>|<length>',
     default => 'baseline',
     inherit => 0,
    },
    
    visibility => {
     format => 'visible|hidden|collapse',
     default => 'visible',
     inherit => 1,
    },
    
   'voice-family' => {
     format => '(male|female|child|<str/words>)
                [, (male|female|child|<str/words>) ]*',
     default => '',
     inherit => 1,
     list => 1,
    },
    
    volume => {
     format => '<number>|<percentage>|silent|x-soft|soft|medium|loud|
                x-loud',
     default => 'medium',
     inherit => 1,
    },
    
   'white-space' =>   {
     format => 'normal|pre|nowrap|pre-wrap|pre-line',
     default => 'normal',
     inherit => 1,
    },
    
    widows => {
     format => '<integer>',
     default => 2,
     inherit => 1,
    },
    
    width => {
     format => '<length>|<percentage>|auto',
     default => 'auto',
     inherit => 0,
    },
    
   'word-spacing' => {
     format => 'normal|<length>',
     default => 'normal',
     inherit => 1,
    },
    

lib/CSS/DOM/Value/Primitive.pm  view on Meta::CPAN

			               $val_obj->primitiveType
			                == CSS_NUMBER
			              ){
			               not $val % 17 and $val == int $val
			                and $val > 0 and $val < 256
			             # ~~~ Would it be faster simply to use
			             #     a regexp?
			                 or undef $ret, last;
			               $ret .= sprintf "%x", $val/17;
			              }
			              else { # percentage
			               not $val % 20 and $val == int $val
			                and $val > 0 and $val < 101
			             # ~~~ Would it be faster simply to use
			             #     a regexp?
			                 or undef $ret, last;
			               $ret .= sprintf "%x", $val * .15;
			              }
			             }
			            }
			            if(!$val || $digits == 2) {

lib/CSS/DOM/Value/Primitive.pm  view on Meta::CPAN

			              my $val = $val_obj->getFloatValue;
			              if(
			               $val_obj->primitiveType
			                == CSS_NUMBER
			              ){
			               $val == int $val
			                and $val > 0 and $val < 256
			                 or undef $ret, last;
			               $ret .= sprintf "%02x", $val;
			              }
			              elsif($digits == 2) { # percentage
			               not $val % 20 and $val == int $val
			                and $val > 0 and $val < 101
			             # ~~~ Would it be faster simply to use
			             #     a regexp?
			                 or undef $ret, last;
			               $ret .= sprintf "%02x",$val * 2.55;
			              }
			             }
			            }
			            $ret and substr $ret,0,0, = '#';

lib/CSS/DOM/Value/Primitive.pm  view on Meta::CPAN

     [type => CSS_NUMBER, value => $_],
     @$rgb
   ];
  }
 }
 for my $val($$self[valu][$index]) {
  if(ref $val eq 'ARRAY') {
   $val = new
    __PACKAGE__,
     owner => $$self[ownr],
     format => $index == 3 ? '<number>' : '<number>|<percentage>',
     @$val;
   delete $$self[csst];
  }
  elsif(!defined $val and $index == 3) { # alpha
   $val = new
    __PACKAGE__,
     owner => $$self[ownr],
     format => '<number>',
     type => CSS_NUMBER,
     value => 1; 

t/CSSPrimitiveValue.t  view on Meta::CPAN

 $v->cssText('50%');
 is $v->cssText, '50%',
  'setting cssText on a sub-value of a colour to 50% works';
 is $v->primitiveType, &CSS::DOM::Value::Primitive::CSS_PERCENTAGE,
  'changing the cssText of a colour’s sub-value changes the prim type';
 like $clr->cssText, qr/^rgb\(127.5,\s*255,\s*238\)\z/,
  'the colour’s cssText after making the subvalues mixed numbers & %’s';
 $v = $clr->alpha;
 $v->cssText('50%');
 is $v->cssText, 1,
  'alpha values ignore assignments of percentage values to cssText';
 $v->cssText(.5);
 is $v->cssText, .5,
  'but number assignments (to alpha values’ cssText) work';
 like $clr->cssText, qr/^rgba\(127.5,\s*255,\s*238,\s*0.5\)\z/,
  'the colour’s cssText after making the subvalues mixed numbers & %’s';

 $v = $s->getPropertyCSSValue('color');
 $v->cssText('activeborder');;
 is $v->primitiveType, &CSS::DOM::Value::Primitive::CSS_IDENT,
  'setting a colour property’s cssText to a sys. colour makes it an ident';

t/CSSValue.t  view on Meta::CPAN

test_value $s,"top","", [type => &CSS_INHERIT], 'inherit', &CSS_INHERIT,
 'inherit';
test_value $s,"background-position", "",
 [type=>&CSS_CUSTOM,value=>"top left"],
 'top left', &CSS_CUSTOM, 'custom value';

use tests 130;
my $css_num = &CSS::DOM::Value::Primitive::CSS_NUMBER;
for( #constant   constructor val arg   prop       css str      test name
 [ number     =>  '73'              , 'z-index', '73'       , 'number'   ],
 [ percentage =>  '73'              , 'top'    , '73%'      , '%'        ],
 [ ems        =>  '73'              , 'top'    , '73em'     , 'em'       ],
 [ exs        =>  '73'              , 'top'    , '73ex'     , 'ex'       ],
 [ px         =>  '73'              , 'top'    , '73px'     , 'px'       ],
 [ cm         =>  '73'              , 'top'    , '73cm'     , 'cm'       ],
 [ mm         =>  '73'              , 'top'    , '73mm'     , 'mm'       ],
 [ in         =>  '73'              , 'top'    , '73in'     , 'inch'     ],
 [ pt         =>  '73'              , 'top'    , '73pt'     , 'point'    ],
 [ pc         =>  '73'              , 'top'    , '73pc'     , 'pica'     ],
 [ deg        =>  '73'              , 'azimuth', '73deg'    , 'degree'   ],
 [ rad        =>  '73'              , 'azimuth', '73rad'    , 'radian'   ],

t/CSSValue.t  view on Meta::CPAN

 $v->cssText('50%');
 is $v->cssText, '50%',
  'setting cssText on a sub-value of a colour to 50% works';
 is $v->primitiveType, &CSS::DOM::Value::Primitive::CSS_PERCENTAGE,
  'changing the cssText of a colour’s sub-value changes the prim type';
 like $clr->cssText, qr/^rgb\(127.5,\s*255,\s*238\)\z/,
  'the colour’s cssText after making the subvalues mixed numbers & %’s';
 $v = $clr->alpha;
 $v->cssText('50%');
 is $v->cssText, 1,
  'alpha values ignore assignments of percentage values to cssText';
 $v->cssText(.5);
 is $v->cssText, .5,
  'but number assignments (to alpha values’ cssText) work';
 like $clr->cssText, qr/^rgba\(127.5,\s*255,\s*238,\s*0.5\)\z/,
  'the colour’s cssText after making the subvalues mixed numbers & %’s';

 $v = $s->getPropertyCSSValue('color');
 $v->cssText('activeborder');;
 is $v->primitiveType, &CSS::DOM::Value::Primitive::CSS_IDENT,
  'setting a colour property’s cssText to a sys. colour makes it an ident';

t/parser-tokens.t  view on Meta::CPAN

	is $rule->href, "", q"'";
	$rule->cssText('@import ' . q'"');
	is $rule->href, '', '"';
}


# ~~~ once both selectors mean something and getCSSPropertyValue is imple-
#     mented, we need #hash tests

# ~~~ numbers
# ~~~ percent
# ~~~ dimensions


use tests 23; # urls
{
	my $rule = new CSS'DOM'Rule::Import;

	$rule->cssText('@import url(!$#%&][\\\}|{*~foo/bar.gif)');
	is $rule->href, '!$#%&][\}|{*~foo/bar.gif', 'unquoted url';
	$rule->cssText('@import url(/*foo*/foo/bar.gif/*bar)');



( run in 0.479 second using v1.01-cache-2.11-cpan-624ce96ca49 )