| 5 |
|
|
| 6 |
sub new ($) { |
sub new ($) { |
| 7 |
my $self = bless {onerror => sub { }, must_level => 'm', |
my $self = bless {onerror => sub { }, must_level => 'm', |
| 8 |
|
message_level => 'w', |
| 9 |
unsupported_level => 'unsupported'}, shift; |
unsupported_level => 'unsupported'}, shift; |
| 10 |
|
|
| 11 |
return $self; |
return $self; |
| 16 |
sub IGNORED_STATEMENT_STATE () { 2 } |
sub IGNORED_STATEMENT_STATE () { 2 } |
| 17 |
sub IGNORED_DECLARATION_STATE () { 3 } |
sub IGNORED_DECLARATION_STATE () { 3 } |
| 18 |
|
|
| 19 |
|
our $Prop; ## By CSS property name |
| 20 |
|
our $Attr; ## By CSSOM attribute name |
| 21 |
|
our $Key; ## By internal key |
| 22 |
|
|
| 23 |
sub parse_char_string ($$) { |
sub parse_char_string ($$) { |
| 24 |
my $self = $_[0]; |
my $self = $_[0]; |
| 25 |
|
|
| 93 |
$t->{type} == CDC_TOKEN; |
$t->{type} == CDC_TOKEN; |
| 94 |
|
|
| 95 |
if ($t->{type} == ATKEYWORD_TOKEN) { |
if ($t->{type} == ATKEYWORD_TOKEN) { |
| 96 |
if ($t->{value} eq 'namespace') { |
if (lc $t->{value} eq 'namespace') { ## TODO: case folding |
| 97 |
$t = $tt->get_next_token; |
$t = $tt->get_next_token; |
| 98 |
$t = $tt->get_next_token while $t->{type} == S_TOKEN; |
$t = $tt->get_next_token while $t->{type} == S_TOKEN; |
| 99 |
|
|
| 143 |
level => $self->{must_level}, |
level => $self->{must_level}, |
| 144 |
token => $t); |
token => $t); |
| 145 |
# |
# |
| 146 |
} elsif ($t->{value} eq 'charset') { |
} elsif (lc $t->{value} eq 'charset') { ## TODO: case folding |
| 147 |
$t = $tt->get_next_token; |
$t = $tt->get_next_token; |
| 148 |
$t = $tt->get_next_token while $t->{type} == S_TOKEN; |
$t = $tt->get_next_token while $t->{type} == S_TOKEN; |
| 149 |
|
|
| 238 |
## NOTE: DELIM? in declaration will be removed: |
## NOTE: DELIM? in declaration will be removed: |
| 239 |
## <http://csswg.inkedblade.net/spec/css2.1?s=declaration%20delim#issue-2>. |
## <http://csswg.inkedblade.net/spec/css2.1?s=declaration%20delim#issue-2>. |
| 240 |
|
|
| 241 |
|
my $prop_def; |
| 242 |
|
my $prop_value; |
| 243 |
|
my $prop_flag; |
| 244 |
$t = $tt->get_next_token while $t->{type} == S_TOKEN; |
$t = $tt->get_next_token while $t->{type} == S_TOKEN; |
| 245 |
if ($t->{type} == IDENT_TOKEN) { # property |
if ($t->{type} == IDENT_TOKEN) { # property |
| 246 |
## TODO: If supported, ... |
my $prop_name = lc $t->{value}; ## TODO: case folding |
|
|
|
| 247 |
$t = $tt->get_next_token; |
$t = $tt->get_next_token; |
| 248 |
# |
if ($t->{type} == COLON_TOKEN) { |
| 249 |
} elsif ($t->{type} == RBRACE_TOKEN) { |
$t = $tt->get_next_token; |
| 250 |
|
$t = $tt->get_next_token while $t->{type} == S_TOKEN; |
| 251 |
|
|
| 252 |
|
$prop_def = $Prop->{$prop_name}; |
| 253 |
|
if ($prop_def and $self->{prop}->{$prop_name}) { |
| 254 |
|
($t, $prop_value) |
| 255 |
|
= $prop_def->{parse}->($self, $prop_name, $tt, $t, $onerror); |
| 256 |
|
if ($prop_value) { |
| 257 |
|
## NOTE: {parse} don't have to consume trailing spaces. |
| 258 |
|
$t = $tt->get_next_token while $t->{type} == S_TOKEN; |
| 259 |
|
|
| 260 |
|
if ($t->{type} == EXCLAMATION_TOKEN) { |
| 261 |
|
$t = $tt->get_next_token; |
| 262 |
|
$t = $tt->get_next_token while $t->{type} == S_TOKEN; |
| 263 |
|
if ($t->{type} == IDENT_TOKEN and |
| 264 |
|
lc $t->{value} eq 'important') { ## TODO: case folding |
| 265 |
|
$prop_flag = 'important'; |
| 266 |
|
|
| 267 |
|
$t = $tt->get_next_token; |
| 268 |
|
$t = $tt->get_next_token while $t->{type} == S_TOKEN; |
| 269 |
|
|
| 270 |
|
# |
| 271 |
|
} else { |
| 272 |
|
$onerror->(type => 'syntax error:important', |
| 273 |
|
level => $self->{must_level}, |
| 274 |
|
token => $t); |
| 275 |
|
|
| 276 |
|
## Reprocess. |
| 277 |
|
$state = IGNORED_DECLARATION_STATE; |
| 278 |
|
redo S; |
| 279 |
|
} |
| 280 |
|
} |
| 281 |
|
|
| 282 |
|
# |
| 283 |
|
} else { |
| 284 |
|
## Syntax error. |
| 285 |
|
|
| 286 |
|
## Reprocess. |
| 287 |
|
$state = IGNORED_DECLARATION_STATE; |
| 288 |
|
redo S; |
| 289 |
|
} |
| 290 |
|
} else { |
| 291 |
|
$onerror->(type => 'not supported:property', |
| 292 |
|
level => $self->{unsupported_level}, |
| 293 |
|
token => $t, value => $prop_name); |
| 294 |
|
|
| 295 |
|
# |
| 296 |
|
$state = IGNORED_DECLARATION_STATE; |
| 297 |
|
redo S; |
| 298 |
|
} |
| 299 |
|
} else { |
| 300 |
|
$onerror->(type => 'syntax error:property colon', |
| 301 |
|
level => $self->{must_level}, |
| 302 |
|
token => $t); |
| 303 |
|
|
| 304 |
|
# |
| 305 |
|
$state = IGNORED_DECLARATION_STATE; |
| 306 |
|
redo S; |
| 307 |
|
} |
| 308 |
|
} |
| 309 |
|
|
| 310 |
|
if ($t->{type} == RBRACE_TOKEN) { |
| 311 |
$t = $tt->get_next_token; |
$t = $tt->get_next_token; |
| 312 |
$state = BEFORE_STATEMENT_STATE; |
$state = BEFORE_STATEMENT_STATE; |
| 313 |
redo S; |
#redo S; |
| 314 |
|
} elsif ($t->{type} == SEMICOLON_TOKEN) { |
| 315 |
|
$t = $tt->get_next_token; |
| 316 |
|
## Stay in the state. |
| 317 |
|
#redo S; |
| 318 |
} elsif ($t->{type} == EOF_TOKEN) { |
} elsif ($t->{type} == EOF_TOKEN) { |
| 319 |
$onerror->(type => 'syntax error:ruleset not closed', |
$onerror->(type => 'syntax error:ruleset not closed', |
| 320 |
level => $self->{must_level}, |
level => $self->{must_level}, |
| 321 |
token => $t); |
token => $t); |
| 322 |
## Reprocess. |
## Reprocess. |
| 323 |
$state = BEFORE_STATEMENT_STATE; |
$state = BEFORE_STATEMENT_STATE; |
| 324 |
|
#redo S; |
| 325 |
|
} else { |
| 326 |
|
if ($prop_value) { |
| 327 |
|
$onerror->(type => 'syntax error:property semicolon', |
| 328 |
|
level => $self->{must_level}, |
| 329 |
|
token => $t); |
| 330 |
|
} else { |
| 331 |
|
$onerror->(type => 'syntax error:property name', |
| 332 |
|
level => $self->{must_level}, |
| 333 |
|
token => $t); |
| 334 |
|
} |
| 335 |
|
|
| 336 |
|
# |
| 337 |
|
$state = IGNORED_DECLARATION_STATE; |
| 338 |
redo S; |
redo S; |
| 339 |
} |
} |
| 340 |
|
|
| 341 |
# |
my $important = (defined $prop_flag and $prop_flag eq 'important'); |
| 342 |
$state = IGNORED_DECLARATION_STATE; |
for my $set_prop_name (keys %{$prop_value or {}}) { |
| 343 |
|
my $set_prop_def = $Prop->{$set_prop_name}; |
| 344 |
|
$$current_decls->{$set_prop_def->{key}} |
| 345 |
|
= [$prop_value->{$set_prop_name}, $prop_flag] |
| 346 |
|
if $important or |
| 347 |
|
not $$current_decls->{$set_prop_def->{key}} or |
| 348 |
|
not defined $$current_decls->{$set_prop_def->{key}}->[1]; |
| 349 |
|
} |
| 350 |
redo S; |
redo S; |
| 351 |
} elsif ($state == IGNORED_STATEMENT_STATE or |
} elsif ($state == IGNORED_STATEMENT_STATE or |
| 352 |
$state == IGNORED_DECLARATION_STATE) { |
$state == IGNORED_DECLARATION_STATE) { |
| 430 |
return $ss; |
return $ss; |
| 431 |
} # parse_char_string |
} # parse_char_string |
| 432 |
|
|
| 433 |
|
$Prop->{color} = { |
| 434 |
|
css => 'color', |
| 435 |
|
dom => 'color', |
| 436 |
|
key => 'color', |
| 437 |
|
parse => sub { |
| 438 |
|
my ($self, $prop_name, $tt, $t, $onerror) = @_; |
| 439 |
|
|
| 440 |
|
if ($t->{type} == IDENT_TOKEN) { |
| 441 |
|
if (lc $t->{value} eq 'blue') { ## TODO: case folding |
| 442 |
|
$t = $tt->get_next_token; |
| 443 |
|
return ($t, {$prop_name => ["RGBA", 0, 0, 255, 1]}); |
| 444 |
|
} else { |
| 445 |
|
# |
| 446 |
|
} |
| 447 |
|
} else { |
| 448 |
|
# |
| 449 |
|
} |
| 450 |
|
|
| 451 |
|
$onerror->(type => 'syntax error:color', |
| 452 |
|
level => $self->{must_level}, |
| 453 |
|
token => $t); |
| 454 |
|
|
| 455 |
|
return ($t, undef); |
| 456 |
|
}, |
| 457 |
|
serialize => sub { |
| 458 |
|
my ($self, $prop_name, $value) = @_; |
| 459 |
|
if ($value->[0] eq 'RGBA') { ## TODO: %d? %f? |
| 460 |
|
return sprintf 'rgba(%d, %d, %d, %f)', @$value[1, 2, 3, 4]; |
| 461 |
|
} else { |
| 462 |
|
return undef; |
| 463 |
|
} |
| 464 |
|
}, |
| 465 |
|
}; |
| 466 |
|
$Attr->{color} = $Prop->{color}; |
| 467 |
|
$Key->{color} = $Prop->{color}; |
| 468 |
|
|
| 469 |
|
my $one_keyword_parser = sub { |
| 470 |
|
my ($self, $prop_name, $tt, $t, $onerror) = @_; |
| 471 |
|
|
| 472 |
|
if ($t->{type} == IDENT_TOKEN) { |
| 473 |
|
my $prop_value = lc $t->{value}; ## TODO: case folding |
| 474 |
|
$t = $tt->get_next_token; |
| 475 |
|
if ($Prop->{$prop_name}->{keyword}->{$prop_value} and |
| 476 |
|
$self->{prop_value}->{$prop_name}->{$prop_value}) { |
| 477 |
|
return ($t, {$prop_name => ["KEYWORD", $prop_value]}); |
| 478 |
|
} elsif ($prop_value eq 'inherit') { |
| 479 |
|
return ($t, {$prop_name => ["KEYWORD", $prop_value]}); |
| 480 |
|
} |
| 481 |
|
} |
| 482 |
|
|
| 483 |
|
$onerror->(type => 'syntax error:keyword:'.$prop_name, |
| 484 |
|
level => $self->{must_level}, |
| 485 |
|
token => $t); |
| 486 |
|
return ($t, undef); |
| 487 |
|
}; |
| 488 |
|
|
| 489 |
|
my $one_keyword_serializer = sub { |
| 490 |
|
my ($self, $prop_name, $value) = @_; |
| 491 |
|
if ($value->[0] eq 'KEYWORD') { |
| 492 |
|
return $value->[1]; |
| 493 |
|
} else { |
| 494 |
|
return undef; |
| 495 |
|
} |
| 496 |
|
}; |
| 497 |
|
|
| 498 |
|
$Prop->{display} = { |
| 499 |
|
css => 'display', |
| 500 |
|
dom => 'display', |
| 501 |
|
key => 'display', |
| 502 |
|
parse => $one_keyword_parser, |
| 503 |
|
serialize => $one_keyword_serializer, |
| 504 |
|
keyword => { |
| 505 |
|
block => 1, inline => 1, 'inline-block' => 1, 'inline-table' => 1, |
| 506 |
|
'list-item' => 1, none => 1, |
| 507 |
|
table => 1, 'table-caption' => 1, 'table-cell' => 1, 'table-column' => 1, |
| 508 |
|
'table-column-group' => 1, 'table-header-group' => 1, |
| 509 |
|
'table-footer-group' => 1, 'table-row' => 1, 'table-row-group' => 1, |
| 510 |
|
}, |
| 511 |
|
}; |
| 512 |
|
$Attr->{display} = $Prop->{display}; |
| 513 |
|
$Key->{display} = $Prop->{display}; |
| 514 |
|
|
| 515 |
|
$Prop->{position} = { |
| 516 |
|
css => 'position', |
| 517 |
|
dom => 'position', |
| 518 |
|
key => 'position', |
| 519 |
|
parse => $one_keyword_parser, |
| 520 |
|
serialize => $one_keyword_serializer, |
| 521 |
|
keyword => { |
| 522 |
|
static => 1, relative => 1, absolute => 1, fixed => 1, |
| 523 |
|
}, |
| 524 |
|
}; |
| 525 |
|
$Attr->{position} = $Prop->{position}; |
| 526 |
|
$Key->{position} = $Prop->{position}; |
| 527 |
|
|
| 528 |
|
$Prop->{float} = { |
| 529 |
|
css => 'float', |
| 530 |
|
dom => 'css_float', |
| 531 |
|
key => 'float', |
| 532 |
|
parse => $one_keyword_parser, |
| 533 |
|
serialize => $one_keyword_serializer, |
| 534 |
|
keyword => { |
| 535 |
|
left => 1, right => 1, none => 1, |
| 536 |
|
}, |
| 537 |
|
}; |
| 538 |
|
$Attr->{css_float} = $Prop->{float}; |
| 539 |
|
$Attr->{style_float} = $Prop->{float}; ## NOTE: IEism |
| 540 |
|
$Key->{float} = $Prop->{float}; |
| 541 |
|
|
| 542 |
|
$Prop->{clear} = { |
| 543 |
|
css => 'clear', |
| 544 |
|
dom => 'clear', |
| 545 |
|
key => 'clear', |
| 546 |
|
parse => $one_keyword_parser, |
| 547 |
|
serialize => $one_keyword_serializer, |
| 548 |
|
keyword => { |
| 549 |
|
left => 1, right => 1, none => 1, both => 1, |
| 550 |
|
}, |
| 551 |
|
}; |
| 552 |
|
$Attr->{clear} = $Prop->{clear}; |
| 553 |
|
$Key->{clear} = $Prop->{clear}; |
| 554 |
|
|
| 555 |
|
$Prop->{direction} = { |
| 556 |
|
css => 'direction', |
| 557 |
|
dom => 'direction', |
| 558 |
|
key => 'direction', |
| 559 |
|
parse => $one_keyword_parser, |
| 560 |
|
serialize => $one_keyword_serializer, |
| 561 |
|
keyword => { |
| 562 |
|
ltr => 1, rtl => 1, |
| 563 |
|
}, |
| 564 |
|
}; |
| 565 |
|
$Attr->{direction} = $Prop->{direction}; |
| 566 |
|
$Key->{direction} = $Prop->{direction}; |
| 567 |
|
|
| 568 |
|
$Prop->{'unicode-bidi'} = { |
| 569 |
|
css => 'unicode-bidi', |
| 570 |
|
dom => 'unicode_bidi', |
| 571 |
|
key => 'unicode_bidi', |
| 572 |
|
parse => $one_keyword_parser, |
| 573 |
|
serialize => $one_keyword_serializer, |
| 574 |
|
keyword => { |
| 575 |
|
normal => 1, embed => 1, 'bidi-override' => 1, |
| 576 |
|
}, |
| 577 |
|
}; |
| 578 |
|
$Attr->{unicode_bidi} = $Prop->{'unicode-bidi'}; |
| 579 |
|
$Key->{unicode_bidi} = $Prop->{'unicode-bidi'}; |
| 580 |
|
|
| 581 |
|
my $border_style_keyword = { |
| 582 |
|
none => 1, hidden => 1, dotted => 1, dashed => 1, solid => 1, |
| 583 |
|
double => 1, groove => 1, ridge => 1, inset => 1, outset => 1, |
| 584 |
|
}; |
| 585 |
|
|
| 586 |
|
$Prop->{'border-top-style'} = { |
| 587 |
|
css => 'border-top-style', |
| 588 |
|
dom => 'border_top_style', |
| 589 |
|
key => 'border_top_style', |
| 590 |
|
parse => $one_keyword_parser, |
| 591 |
|
serialize => $one_keyword_serializer, |
| 592 |
|
keyword => $border_style_keyword, |
| 593 |
|
}; |
| 594 |
|
$Attr->{border_top_style} = $Prop->{'border-top-style'}; |
| 595 |
|
$Key->{border_top_style} = $Prop->{'border-top-style'}; |
| 596 |
|
|
| 597 |
|
$Prop->{'border-right-style'} = { |
| 598 |
|
css => 'border-right-style', |
| 599 |
|
dom => 'border_right_style', |
| 600 |
|
key => 'border_right_style', |
| 601 |
|
parse => $one_keyword_parser, |
| 602 |
|
serialize => $one_keyword_serializer, |
| 603 |
|
keyword => $border_style_keyword, |
| 604 |
|
}; |
| 605 |
|
$Attr->{border_right_style} = $Prop->{'border-right-style'}; |
| 606 |
|
$Key->{border_right_style} = $Prop->{'border-right-style'}; |
| 607 |
|
|
| 608 |
|
$Prop->{'border-bottom-style'} = { |
| 609 |
|
css => 'border-bottom-style', |
| 610 |
|
dom => 'border_bottom_style', |
| 611 |
|
key => 'border_bottom_style', |
| 612 |
|
parse => $one_keyword_parser, |
| 613 |
|
serialize => $one_keyword_serializer, |
| 614 |
|
keyword => $border_style_keyword, |
| 615 |
|
}; |
| 616 |
|
$Attr->{border_bottom_style} = $Prop->{'border-bottom-style'}; |
| 617 |
|
$Key->{border_bottom_style} = $Prop->{'border-bottom-style'}; |
| 618 |
|
|
| 619 |
|
$Prop->{'border-left-style'} = { |
| 620 |
|
css => 'border-left-style', |
| 621 |
|
dom => 'border_left_style', |
| 622 |
|
key => 'border_left_style', |
| 623 |
|
parse => $one_keyword_parser, |
| 624 |
|
serialize => $one_keyword_serializer, |
| 625 |
|
keyword => $border_style_keyword, |
| 626 |
|
}; |
| 627 |
|
$Attr->{border_left_style} = $Prop->{'border-left-style'}; |
| 628 |
|
$Key->{border_left_style} = $Prop->{'border-left-style'}; |
| 629 |
|
|
| 630 |
|
$Prop->{'border-style'} = { |
| 631 |
|
css => 'border-style', |
| 632 |
|
dom => 'border_style', |
| 633 |
|
parse => sub { |
| 634 |
|
my ($self, $prop_name, $tt, $t, $onerror) = @_; |
| 635 |
|
|
| 636 |
|
my %prop_value; |
| 637 |
|
my $has_inherit; |
| 638 |
|
if ($t->{type} == IDENT_TOKEN) { |
| 639 |
|
my $prop_value = lc $t->{value}; ## TODO: case folding |
| 640 |
|
$t = $tt->get_next_token; |
| 641 |
|
if ($border_style_keyword->{$prop_value} and |
| 642 |
|
$self->{prop_value}->{'border-top-style'}->{$prop_value}) { |
| 643 |
|
$prop_value{'border-top-style'} = ["KEYWORD", $prop_value]; |
| 644 |
|
} elsif ($prop_value eq 'inherit') { |
| 645 |
|
$prop_value{'border-top-style'} = ["KEYWORD", $prop_value]; |
| 646 |
|
$has_inherit = 1; |
| 647 |
|
} else { |
| 648 |
|
$onerror->(type => 'syntax error:keyword:'.$prop_name, |
| 649 |
|
level => $self->{must_level}, |
| 650 |
|
token => $t); |
| 651 |
|
return ($t, undef); |
| 652 |
|
} |
| 653 |
|
$prop_value{'border-right-style'} = $prop_value{'border-top-style'}; |
| 654 |
|
$prop_value{'border-bottom-style'} = $prop_value{'border-top-style'}; |
| 655 |
|
$prop_value{'border-left-style'} = $prop_value{'border-right-style'}; |
| 656 |
|
} else { |
| 657 |
|
$onerror->(type => 'syntax error:keyword:'.$prop_name, |
| 658 |
|
level => $self->{must_level}, |
| 659 |
|
token => $t); |
| 660 |
|
return ($t, undef); |
| 661 |
|
} |
| 662 |
|
|
| 663 |
|
$t = $tt->get_next_token while $t->{type} == S_TOKEN; |
| 664 |
|
if ($t->{type} == IDENT_TOKEN) { |
| 665 |
|
my $prop_value = lc $t->{value}; ## TODO: case folding |
| 666 |
|
$t = $tt->get_next_token; |
| 667 |
|
if (not $has_inherit and |
| 668 |
|
$border_style_keyword->{$prop_value} and |
| 669 |
|
$self->{prop_value}->{'border-right-style'}->{$prop_value}) { |
| 670 |
|
$prop_value{'border-right-style'} = ["KEYWORD", $prop_value]; |
| 671 |
|
} else { |
| 672 |
|
$onerror->(type => 'syntax error:keyword:'.$prop_name, |
| 673 |
|
level => $self->{must_level}, |
| 674 |
|
token => $t); |
| 675 |
|
return ($t, undef); |
| 676 |
|
} |
| 677 |
|
$prop_value{'border-left-style'} = $prop_value{'border-right-style'}; |
| 678 |
|
|
| 679 |
|
$t = $tt->get_next_token while $t->{type} == S_TOKEN; |
| 680 |
|
if ($t->{type} == IDENT_TOKEN) { |
| 681 |
|
my $prop_value = lc $t->{value}; ## TODO: case folding |
| 682 |
|
$t = $tt->get_next_token; |
| 683 |
|
if ($border_style_keyword->{$prop_value} and |
| 684 |
|
$self->{prop_value}->{'border-bottom-style'}->{$prop_value}) { |
| 685 |
|
$prop_value{'border-bottom-style'} = ["KEYWORD", $prop_value]; |
| 686 |
|
} else { |
| 687 |
|
$onerror->(type => 'syntax error:keyword:'.$prop_name, |
| 688 |
|
level => $self->{must_level}, |
| 689 |
|
token => $t); |
| 690 |
|
return ($t, undef); |
| 691 |
|
} |
| 692 |
|
|
| 693 |
|
$t = $tt->get_next_token while $t->{type} == S_TOKEN; |
| 694 |
|
if ($t->{type} == IDENT_TOKEN) { |
| 695 |
|
my $prop_value = lc $t->{value}; ## TODO: case folding |
| 696 |
|
$t = $tt->get_next_token; |
| 697 |
|
if ($border_style_keyword->{$prop_value} and |
| 698 |
|
$self->{prop_value}->{'border-left-style'}->{$prop_value}) { |
| 699 |
|
$prop_value{'border-left-style'} = ["KEYWORD", $prop_value]; |
| 700 |
|
} else { |
| 701 |
|
$onerror->(type => 'syntax error:keyword:'.$prop_name, |
| 702 |
|
level => $self->{must_level}, |
| 703 |
|
token => $t); |
| 704 |
|
return ($t, undef); |
| 705 |
|
} |
| 706 |
|
} |
| 707 |
|
} |
| 708 |
|
} |
| 709 |
|
|
| 710 |
|
return ($t, \%prop_value); |
| 711 |
|
}, |
| 712 |
|
serialize => sub { |
| 713 |
|
my ($self, $prop_name, $value) = @_; |
| 714 |
|
|
| 715 |
|
local $Error::Depth = $Error::Depth + 1; |
| 716 |
|
my @v; |
| 717 |
|
push @v, $self->border_top_style; |
| 718 |
|
return undef unless defined $v[-1]; |
| 719 |
|
push @v, $self->border_right_style; |
| 720 |
|
return undef unless defined $v[-1]; |
| 721 |
|
push @v, $self->border_bottom_style; |
| 722 |
|
return undef unless defined $v[-1]; |
| 723 |
|
push @v, $self->border_bottom_style; |
| 724 |
|
return undef unless defined $v[-1]; |
| 725 |
|
|
| 726 |
|
pop @v if $v[1] eq $v[3]; |
| 727 |
|
pop @v if $v[0] eq $v[2]; |
| 728 |
|
pop @v if $v[0] eq $v[1]; |
| 729 |
|
return join ' ', @v; |
| 730 |
|
}, |
| 731 |
|
}; |
| 732 |
|
$Attr->{border_style} = $Prop->{'border-style'}; |
| 733 |
|
|
| 734 |
1; |
1; |
| 735 |
## $Date$ |
## $Date$ |