HTML-Selector-XPath

 view release on metacpan or  search on metacpan

lib/HTML/Selector/XPath.pm  view on Meta::CPAN

    } elsif ($op eq '$=') {
        my $n = length($^N) - 1;
        "substring(\@$left,string-length(\@$left)-$n)='$^N'";
    } else { # exact match
        "\@$left='$^N'";
    }
}

sub _generate_child {
    my ($direction,$name,$a,$b) = @_;
    if ($a == 0) { # 0n+b
        $b--;
        "[count($direction-sibling::$name) = $b and parent::*]"
    } elsif ($a > 0) { # an + b
        return "[not((count($direction-sibling::$name)+1)<$b) and ((count($direction-sibling::$name) + 1) - $b) mod $a = 0 and parent::*]"
    } else { # -an + $b
        $a = -$a;
        return "[not((count($direction-sibling::$name)+1)>$b) and (($b - (count($direction-sibling::$name) + 1)) mod $a) = 0 and parent::*]"
    }
}

sub nth_child {
    my ($a,$b) = @_;
    if (@_ == 1) {
        ($a,$b) = (0,$a);
    }
    _generate_child('preceding', '*', $a, $b);
}

sub nth_last_child {
    my ($a,$b) = @_;
    if (@_ == 1) {
        ($a,$b) = (0,$a);
    }
    _generate_child('following', '*', $a, $b);
}

# A hacky recursive descent
# Only descends for :not(...)
sub consume_An_plus_B {
    my( $rrule ) = @_;

    my( $A, $B );

    if( $$rrule =~ s/^odd\s*\)// ) {
        ($A,$B) = (2, 1)
    } elsif( $$rrule =~ s/^even\s*\)// ) {
        ($A,$B) = (2, 0)
    } elsif( $$rrule =~ s/^\s*(-?\d+)\s*\)// ) {
        ($A,$B) = (0, $1)
    } elsif( $$rrule =~ s/^\s*(-?\d*)\s*n\s*(?:\+\s*(\d+))?\s*\)// ) {
        ($A,$B) = ($1, $2 || 0);
        if( ! defined $A ) {
            $A = '0';
        } elsif( $A eq '-') {
            $A = '-1';
        } elsif( $A eq '' ) {
            $A = '1';
        }
    } else {
        croak "Can't parse formula from '$$rrule'";
    }

    return ($A, $B);
}

sub consume {
    my ($self, $rule, %parms) = @_;
    my $root = $parms{root} || '/';

    return [$rule,''] if $rule =~ m!^/!; # If we start with a slash, we're already an XPath?!

    my @parts = ("$root/");
    my $last_rule = '';
    my @next_parts;

    my $wrote_tag;
    my $root_index = 0; # points to the current root
    # Loop through each "unit" of the rule
    while (length $rule && $rule ne $last_rule) {
        $last_rule = $rule;

        $rule =~ s/^\s*|\s*$//g;
        last unless length $rule;

        # Prepend explicit first selector if we have an implicit selector
        # (that is, if we start with a combinator)
        if ($rule =~ /$reg->{combinator}/) {
            $rule = "* $rule";
        }

        # Match elements
        if ($rule =~ s/$reg->{element}//) {
            my ($id_class,$name,$lang) = ($1,$2,$3);

            # to add *[1]/self:: for follow-sibling
            if (@next_parts) {
                push @parts, @next_parts; #, (pop @parts);
                @next_parts = ();
            }

            my $tag = $id_class eq '' ? $name || '*' : '*';

            if (defined $parms{prefix} and not $tag =~ /[*:|]/) {
                $tag = join ':', $parms{prefix}, $tag;
            }

            if (! $wrote_tag++) {
                push @parts, $tag;
            }

            # XXX Shouldn't the RE allow both, ID and class?
            if ($id_class eq '#') { # ID
                push @parts, "[\@id='$name']";
            } elsif ($id_class eq '.') { # class
                push @parts, "[contains(concat(' ', normalize-space(\@class), ' '), ' $name ')]";
            };
        };

        # Match attribute selectors
        if ($rule =~ s/$reg->{attr2}//) {



( run in 2.702 seconds using v1.01-cache-2.11-cpan-d7f47b0818f )