CSS-SAC
view release on metacpan or search on metacpan
($rule,$css,undef) = Text::Balanced::extract_bracketed($css,q/{}'"/,qr/\s*/); #"
$sac->[_dh_]->start_font_face if $sac->[_dh_can_]->{start_font_face};
$sac->parse_rule(\$rule);
$sac->[_dh_]->end_font_face if $sac->[_dh_can_]->{end_font_face};
}
# @page
elsif ($css =~ s/^\s*\@page\s+//i) {
warn "[SAC] parsing page\n" if DEBUG;
# grab the name and pseudo-page if they're there
$css =~ s/^($RE_IDENT)?\s*(?::($RE_IDENT))?//;
my ($name,$pseudo) = ($1,$2);
# parse the block
my $rule;
($rule,$css,undef) = Text::Balanced::extract_bracketed($css,q/{}'"/,qr/\s*/); #"
$sac->[_dh_]->start_page($name,$pseudo) if $sac->[_dh_can_]->{start_page};
$sac->parse_rule(\$rule);
$sac->[_dh_]->end_page($name,$pseudo) if $sac->[_dh_can_]->{end_page};
}
# unknown
# this is a little harder because we have to guess what
# at rules that we know nothing about will be like
else {
my $at_rule;
warn "[SAC] parsing unknown at rule ($css)\n" if DEBUG;
# take off the @rule first
$css =~ s/^\s*(\@$RE_IDENT\s*)//;
$at_rule .= $1;
# then grab whatever is not a block
$css =~ s/^([^;{]*)//;
$at_rule .= $1;
# now we either terminate, or have a block
if ($css =~ s/^;//) {
$at_rule .= ';';
}
else {
my $block;
($block,$css,undef) = Text::Balanced::extract_bracketed($css,q/{}'"/,qr/\s*/); #"
$at_rule .= $block;
}
$sac->[_dh_]->ignorable_at_rule($at_rule) if $sac->[_dh_can_]->{ignorable_at_rule};
}
}
# html end comment
elsif ($css =~ s/^\s*-->\s*//) {
# we don't do anything with those presently
warn "[SAC] removing HTML comments\n" if DEBUG;
}
# we have selectors
elsif (my $sel_list = $sac->parse_selector_list(\$css)) {
warn "[SAC] parsed selectors\n" if DEBUG;
next unless @$sel_list;
# callbacks
$sac->[_dh_]->start_selector($sel_list) if $sac->[_dh_can_]->{start_selector};
# parse the rule
my $rule;
warn "[SAC] parsing rule\n" if DEBUG;
### BUG
# The Text::Balanced extractions below are not correct since they don't take
# comments into account. With the first one, it'll fail on apostrophes in
# comments, with the latter, on unbalanced apos and } in comments and
# apos-strings. The latter is used currently because it is less likely to fail,
# but what is needed is a real parser that steps inside the black parsing out
# comments and property values. The good news is that we have most of the bits
# to do that right already.
#($rule,$css,undef) = Text::Balanced::extract_bracketed($css,q/{}'"/,qr/\s*/); #"
($rule,$css,undef) = Text::Balanced::extract_bracketed($css,q/{}"/,qr/\s*/); #"
$sac->parse_rule(\$rule);
# end of the rule
$sac->[_dh_]->end_selector($sel_list) if $sac->[_dh_can_]->{end_selector};
}
# trailing whitespace, should only happen at the very end
elsif ($css =~ s/^\s+//) {
# do nothing
warn "[SAC] just whitespace\n" if DEBUG;
}
# error
else {
last if ! length $css;
$sac->[_eh_]->fatal_error('Unknown trailing tokens in style sheet: "' . $css . '"');
last;
}
}
# end doc
warn "[SAC] end of document\n" if DEBUG;
$sac->[_dh_]->end_document if $sac->[_dh_can_]->{end_document};
#---> Finish Parsing <--------------------------------------------#
}
#---------------------------------------------------------------------#
*CSS::SAC::parseStyleSheet = \&parse;
#---------------------------------------------------------------------#
# $sac->parse_charset($string_ref)
# parses a charset
#---------------------------------------------------------------------#
sub parse_charset {
my $sac = shift;
my $css = shift;
# we don't remove leading ws, the charset must come first
return unless $$css =~ s/^\@charset\s+//i;
# extract the string
});
# generate a stream of events
$sac->parse({ filename => 'foo.css' });
=head1 DESCRIPTION
SAC (Simple API for CSS) is an event-based API much like SAX for XML.
If you are familiar with the latter, you should have little trouble
getting used to SAC. More information on SAC can be found online at
http://www.w3.org/TR/SAC.
CSS having more constructs than XML, core SAC is still more complex
than core SAX. However, if you need to parse a CSS style sheet, SAC
probably remains the easiest way to get it done.
Most of the spec is presently implemented. The following interfaces
are not yet there: Locator, CSSException, CSSParseException,
ParserFactory. They may or may not be implemented at a later date
(the most likely candidates are the exception classes, for which I
still have to find an appropriate model).
Some places differ slightly from what is in the spec. I have tried to
keep those to a justified minimum and to flag them correctly.
=head2 the CSS::SAC module itself
The Parser class doesn't exist separately, it's defined in CSS::SAC.
It doesn't expose the locale interface because we don't localize
errors (yet). It also doesn't have C<parse_style_sheet> but rather
C<parse>, which is more consistent with other Perl parsing interfaces.
I have added the C<charset($charset)> callback to the DocumentHandler
interface. There are valid reasons why it wasn't there (it can be
trusted only ever so often, and one should look at the actual encoding
instead) but given that it's a token in the grammar, I believe that
there should still be a way to access it.
=head1 METHODS
=over 4
=item * CSS::SAC->new(\%options) or $sac->new(\%options)
Constructs a new parser object. The options can be:
- ConditionFactory and SelectorFactory
the factory classes used to build selector and condition objects.
See CSS::SAC::{Condition,Selector}Factory for more details on the
interfaces those classes must expose.
- DocumentHandler and ErrorHandler
the handler classes used as sinks for the event stream received
from a SAC Driver. See CSS::SAC::{Document,Error}Factory for more
details on the interfaces those classes must expose.
Methods will be called on whatever it is you pass as values to those
options. Thus, you may pass in objects as well as class names (I
haven't tested this yet, there may be a problem).
NOTE: an error handler should implement all callbacks, while a document
handler may only implement those it is interested in. There is a default
error handler (which dies and warns depending on the type of error) but
not default document handler.
=item * $sac->ParserVerion or $sac->getParserVerion
Returns the supported CSS version.
Requesting this parser's ParserVersion will return the string 'CSS3'.
While that is (modulo potential bugs of course) believed to be
generally true, several caveats apply:
To begin with, CSS3 has been modularised, and various modules are at
different stages of development. Evolving modules may require evolving
this parser. I hesitated between making ParserVersion return CSS2,
CSS3-pre, or simply CSS3. I chose the latter because I intend to
update it as I become aware of the necessity of changes to accommodate
new CSS3 stuff, and because it already supports a number of constructs
alien to CSS2 (of which namespaces is imho important enough to justify
a CSS3 tag). If you are aware of incompatibilities, please contact me.
More importantly, it is now considered wrong for a parser to return
CSSx as its version and instead it is expected to return an uri
corresponding to the uri of the CSS version that it supports. However,
there is no uri for CSS3, but instead one uri per module. While this
issue hasn't been resolved by the WG, I will stick to returning CSS3.
However, B<the behaviour of this attribute is certain to change> in
the future, so please avoid relying on it.
=item * $cf = $sac->ConditionFactory
=item * $sac->ConditionFactory($cf) or $sac->setConditionFactory($cf)
=item * $cf = $sac->SelectorFactory
=item * $sac->SelectorFactory($sf) or $sac->setSelectorFactory($sf)
=item * $cf = $sac->DocumentHandler
=item * $sac->DocumentHandler($dh) or $sac->setDocumentHandler($dh)
=item * $cf = $sac->ErrorHandler
=item * $sac->ErrorHandler($eh) or $sac->setErrorHandler($eh)
get/set the ConditionFactory, SelectorFactory, DocumentHandler,
ErrorHandler that we use
=item * $sac->parse(\%options)
=item * $sac->parseStyleSheet(\%options)
parses a style sheet and sends events to the defined handlers. The
options that you can use are:
=over 8
=item * string
=item * ioref
( run in 1.646 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )