| 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) { |
| 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 |
# |
if ($prop_value) { |
| 342 |
$state = IGNORED_DECLARATION_STATE; |
$$current_decls->{$prop_def->{key}} = [$prop_value, $prop_flag]; |
| 343 |
|
} |
| 344 |
redo S; |
redo S; |
| 345 |
} elsif ($state == IGNORED_STATEMENT_STATE or |
} elsif ($state == IGNORED_STATEMENT_STATE or |
| 346 |
$state == IGNORED_DECLARATION_STATE) { |
$state == IGNORED_DECLARATION_STATE) { |
| 424 |
return $ss; |
return $ss; |
| 425 |
} # parse_char_string |
} # parse_char_string |
| 426 |
|
|
| 427 |
|
$Prop->{color} = { |
| 428 |
|
css => 'color', |
| 429 |
|
dom => 'color', |
| 430 |
|
key => 'color', |
| 431 |
|
parse => sub { |
| 432 |
|
my ($self, $prop_name, $tt, $t, $onerror) = @_; |
| 433 |
|
|
| 434 |
|
if ($t->{type} == IDENT_TOKEN) { |
| 435 |
|
if (lc $t->{value} eq 'blue') { ## TODO: case folding |
| 436 |
|
$t = $tt->get_next_token; |
| 437 |
|
return ($t, ["RGBA", 0, 0, 255, 0]); |
| 438 |
|
} else { |
| 439 |
|
# |
| 440 |
|
} |
| 441 |
|
} else { |
| 442 |
|
# |
| 443 |
|
} |
| 444 |
|
|
| 445 |
|
$onerror->(type => 'syntax error:color', |
| 446 |
|
level => $self->{must_level}, |
| 447 |
|
token => $t); |
| 448 |
|
|
| 449 |
|
return ($t, undef); |
| 450 |
|
}, |
| 451 |
|
serialize => sub { |
| 452 |
|
my ($self, $prop_name, $value) = @_; |
| 453 |
|
if ($value->[0] eq 'RGBA') { ## TODO: %d? %f? |
| 454 |
|
return sprintf 'rgba(%d, %d, %d, %f)', @$value[1, 2, 3, 4]; |
| 455 |
|
} else { |
| 456 |
|
return undef; |
| 457 |
|
} |
| 458 |
|
}, |
| 459 |
|
}; |
| 460 |
|
$Attr->{color} = $Prop->{color}; |
| 461 |
|
$Key->{color} = $Prop->{color}; |
| 462 |
|
|
| 463 |
1; |
1; |
| 464 |
## $Date$ |
## $Date$ |