CSS-Inliner

 view release on metacpan or  search on metacpan

lib/CSS/Inliner.pm  view on Meta::CPAN

=cut

sub inlinify {
  my ($self,$params) = @_;

  $self->_check_object();

  $self->_content_warnings({}); # overwrite any existing warnings

  unless ($self->_html() && $self->_html_tree()) {
    croak 'You must instantiate and read in your content before inlinifying';
  }

  # perform a check to see how bad this html is...
  $self->_validate_html({ html => $self->_html() });

  my $html;
  if (defined $self->_css()) {
    #parse and store the stylesheet as a hash object

    $self->_css->read({ css => $self->_stylesheet() });

    my @css_warnings = @{$self->_css->content_warnings()};
    foreach my $css_warning (@css_warnings) {
      $self->_report_warning({ info => $css_warning });
    }

    my %matched_elements;
    my $count = 0;

    foreach my $entry (@{$self->_css->get_rules()}) {
      next unless exists $$entry{selector} && $$entry{declarations};

      my $selector = $$entry{selector};
      my $declarations = $$entry{declarations};

      #skip over the following pseudo selectors, these particular ones are not inlineable
      if ($selector =~ /(?:^|[\w\._\*\]])::?(?:([\w\-]+))\b/io && $1 !~ /first-child|last-child/i) {
        $self->_report_warning({ info => "The pseudo-class ':$1' cannot be supported inline" });
        next;
      }

      #skip over @import or anything else that might start with @ - not inlineable
      if ($selector =~ /^\@/io) {
        $self->_report_warning({ info => "The directive '$selector' cannot be supported inline" });
        next;
      }

      my $query_result;

      #check to see if query fails, possible for jacked selectors
      eval {
        $query_result = $self->query({ selector => $selector });
      };

      if ($@) {
        $self->_report_warning({ info => $@->info() });
        next;
      }

      # CSS rules cascade based on the specificity and order
      my $specificity = $self->specificity({ selector => $selector });

      #if an element matched a style within the document store the rule, the specificity
      #and the actually CSS attributes so we can inline it later
      foreach my $element (@{$query_result->get_elements()}) {

       $matched_elements{$element->address()} ||= [];
        my %match_info = (
          rule     => $selector,
          element  => $element,
          specificity   => $specificity,
          position => $count,
          css      => $declarations
         );

        push(@{$matched_elements{$element->address()}}, \%match_info);
        $count++;
      }
    }

    #process all elements
    foreach my $matches (values %matched_elements) {
      my $element = $matches->[0]->{element};
      # rules are sorted by specificity, and if there's a tie the position is used
      # we sort with the lightest items first so that heavier items can override later
      my @sorted_matches = sort { $a->{specificity} <=> $b->{specificity} || $a->{position} <=> $b->{position} } @$matches;

      my @new_style;
      my @new_important_style;

      foreach my $match (@sorted_matches) {
        push @new_style, @{$match->{css}};
        push @new_important_style, _grep_important_declarations($match->{css});
      }

      # styles already inlined have greater precedence
      if (defined($element->attr('style'))) {
        my $cur_style = $self->_split(
            {
                style => $self->_encode_entities
                ? decode_entities($element->attr('style'))
                : $element->attr('style')
            }
        );
        push @new_style, @$cur_style;
        push @new_important_style, _grep_important_declarations($cur_style);
      }

      # override styles with !important styles
      push @new_style, @new_important_style;

      my $new_style = $self->_expand({ declarations => \@new_style });
      $element->attr('style', $self->_encode_entities ? encode_entities($new_style) : $new_style);
    }

    #at this point we have a document that contains the expanded inlined stylesheet
    #BUT we need to collapse the declarations to remove duplicate overridden styles
    $self->_collapse_inline_styles();

    # dump out the final processed html for returning to the caller



( run in 1.538 second using v1.01-cache-2.11-cpan-e93a5daba3e )