From de749c09676a4c77cc44edfa31055eec570249ed Mon Sep 17 00:00:00 2001 From: "Kevin H. Kamel" Date: Wed, 8 Aug 2012 20:54:30 +0000 Subject: [PATCH] MM-6593 merge in changes from CPAN contributor Sebastian Paaske Torholm to add rudimentary support for pseudo-selectors. Add tests provided by contributor. Update manifest Update changelog --- ChangeLog | 8 +++++ MANIFEST | 2 ++ lib/HTML/Query.pm | 76 +++++++++++++++++++++++++++++++++++++-- t/html/pseudoclasses.html | 24 +++++++++++++ t/pseudoclasses.t | 42 ++++++++++++++++++++++ t/specificity.t | 5 +-- 6 files changed, 153 insertions(+), 4 deletions(-) create mode 100644 t/html/pseudoclasses.html create mode 100644 t/pseudoclasses.t diff --git a/ChangeLog b/ChangeLog index 332db58..ca8bc69 100644 --- a/ChangeLog +++ b/ChangeLog @@ -48,8 +48,16 @@ * Added skipping for '* html' hack sequence frequently used by IE6, should return nothing 0.07 2010-11-02 Kevin Kamel + * Merge in specificity calculation. This code was ripped out of CSS::Inliner, HTML::Query is a more appropriate place to do this calculation. * Added optimizations so that the specificity calculation doesn't need a tree in order to operate nor does it need a prior query to have occurred. * Merge in tests from CSS::Inliner for specificity calculations + +0.08 2012-08-01 Kevin Kamel + * Added patch from Sebastian Paaske Torholm + - Adds support for -child pseudo-classes including proper specificity calculation + - Adds tests for verifying specificity calculation and proper selection of -child pseudo classes + * Updated documentation with sections for pseudo-classes and psuedo-elements detailing + current support for those features. diff --git a/MANIFEST b/MANIFEST index ea73895..5de76fd 100644 --- a/MANIFEST +++ b/MANIFEST @@ -10,12 +10,14 @@ t/duplicate.t t/element.t t/iterative.t t/multioperand.t +t/pseudoclasses.t t/query.t t/specificity.t t/universal.t t/html/acidtest.html t/html/combinator.html t/html/multioperator.html +t/html/pseudoclasses.html t/html/test1.html t/html/test2.html t/html/test3.html diff --git a/lib/HTML/Query.pm b/lib/HTML/Query.pm index b3d9438..af05893 100644 --- a/lib/HTML/Query.pm +++ b/lib/HTML/Query.pm @@ -271,6 +271,20 @@ sub query { push( @args, $attribute => qr/.*/ ); } } + # and/or one or more pseudo-classes + if ($query =~ / \G : ([\w\-]+) /cgx) { + my $pseudoclass = $1; + $specificity += 10; + + if ($pseudoclass eq 'first-child') { + push( @args, sub { ! grep { ref $_ } $_[0]->left() } ); + } elsif ($pseudoclass eq 'last-child') { + push( @args, sub { ! grep { ref $_ } $_[0]->right() } ); + } else { + warn "Pseudoclass :$pseudoclass not supported"; + next; + } + } # keep going until this particular expression is fully processed last unless scalar(@args) > $work; @@ -1092,9 +1106,67 @@ with the C<+> character. @elems = $query->query('img + span')->get_elements(); -=head2 Combining Selectors +=head2 Pseudo-classes -You can combine basic and hierarchical selectors into a single query +W3C CSS 2 and CSS 3 specifications define new concepts of pseudo-classes to +permit formatting based on information that lies outside the document tree. +See the following link for the most recent spec: +L + +HTML::Query currently has limited support for CSS 2, and no support for CSS 3. + +Patches are *highly* encouraged to help add support here. + +=head3 -child pseudo-classes + +If you want to return child elements within a certain position then -child +pseudo-classes (:first-child, :last-child) are what you're looking for. + + @elems = $query->query('table td:first-child')->get_elements; + +=head3 Link pseudo-classes: :link and :visited + +Unsupported. + +The :link pseudo-class is to be implemented, currently unsupported. + +It is not possible to locate :visited outside of a browser context due to it's +dynamic nature. + +=head3 Dynamic pseudo-classes + +Unsupported. + +It is not possible to locate these classes(:hover, :active, :focus) outside +of a browser context due to their dynamic nature. + +=head3 Language pseudo-class + +Unsupported. + +Functionality for the :lang psuedo-class is largely replicated by using an +attribute selector for lang combined with a universal selector query. + +If this is insufficient I'd love to see a patch adding support for it. + +=head3 Other pseudo-classes + +W3C CSS 3 added a number of new behaviors that need support. At +this time there is no support for them, but we should work on adding support. + +Patches are very welcome. + +=head2 Pseudo-elements + +W3C CSS 2 and CSS 3 specification defines new concepts of pseudo-elements to +permit formatting based on information that lies outside the document tree. +See the following link for the most recent spec: +L + +At this time there is no support for pseudo-elements, but we are working +on adding support. + +Patches are very welcome. =head2 Combining Selectors diff --git a/t/html/pseudoclasses.html b/t/html/pseudoclasses.html new file mode 100644 index 0000000..7e1a307 --- /dev/null +++ b/t/html/pseudoclasses.html @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + +
1,11,21,3
2,12,22,3
3,13,23,3
+ + diff --git a/t/pseudoclasses.t b/t/pseudoclasses.t new file mode 100644 index 0000000..6bca295 --- /dev/null +++ b/t/pseudoclasses.t @@ -0,0 +1,42 @@ +use strict; +use warnings; +use lib qw( ./lib ../lib ); +use HTML::TreeBuilder; +use Badger::Filesystem '$Bin Dir'; +use Badger::Test + tests => 10, + debug => 'HTML::Query', + args => \@ARGV; + +use HTML::Query 'Query'; + +our $Query = 'HTML::Query'; +our $Builder = 'HTML::TreeBuilder'; +our $test_dir = Dir($Bin); +our $html_dir = $test_dir->dir('html')->must_exist; +our $pseudo = $html_dir->file('pseudoclasses.html')->must_exist; + +my ($query, $tree); + +$tree = $Builder->new; +$tree->parse_file( $pseudo->absolute ); + +ok( $tree, 'parsed tree for test file: ' . $pseudo->name ); +$query = Query $tree; +ok( $query, 'created query' ); + +my $test1 = $query->query('table td:first-child'); +is( $test1->size, 3, 'test1 - size' ); +is( join(" | ", $test1->as_trimmed_text), "1,1 | 2,1 | 3,1", 'test1 - text'); + +my $test2 = $query->query('table td:last-child'); +is( $test2->size, 3, 'test2 - size' ); +is( join(" | ", $test2->as_trimmed_text), "1,3 | 2,3 | 3,3", 'test2 - text'); + +my $test3 = $query->query('table tr:first-child td'); +is( $test3->size, 3, 'test3 - size' ); +is( join(" | ", $test3->as_trimmed_text), "1,1 | 1,2 | 1,3", 'test3 - text'); + +my $test4 = $query->query('table tr:last-child td'); +is( $test4->size, 3, 'test4 - size' ); +is( join(" | ", $test4->as_trimmed_text), "3,1 | 3,2 | 3,3", 'test4 - text'); diff --git a/t/specificity.t b/t/specificity.t index ecefa14..880d669 100644 --- a/t/specificity.t +++ b/t/specificity.t @@ -14,7 +14,7 @@ use warnings; use lib qw( ./lib ../lib ); use Badger::Filesystem '$Bin Dir'; use Badger::Test - tests => 23, + tests => 24, debug => 'HTML::Query', args => \@ARGV; @@ -44,7 +44,8 @@ my %rules = ( "*" => 0, "div#id-one div p>em" => 104, "html#simple body#internal" => 202, - "body#internal" => 101 + "body#internal" => 101, + "div:first-child" => 11, ); foreach my $rule (keys %rules) {