Parse-BBCode

 view release on metacpan or  search on metacpan

lib/Parse/BBCode.pm  view on Meta::CPAN

                    else {
                        $content = $post_processor->($content);
                    }
                    $content =~ s/\r?\n|\r/<br>\n/g if $linebreaks;
                    $content;
                };
            }
            else {
                $plain = sub {
                    my ($parser, $attr, $content, $info) = @_;
                    my $text = $post_processor->($content, $info);
                    $text =~ s/\r?\n|\r/<br>\n/g if $linebreaks;
                    $text;
                };
            }
            $self->set_plain($plain);
        }

        # now compile the rest of definitions
        for my $key (keys %$defs) {
            my $def = $defs->{$key};
            #warn __PACKAGE__.':'.__LINE__.": $key: $def\n";
            if (not ref $def) {
                my $new_def = $self->_compile_def($def);
                $defs->{$key} = $new_def;
            }
            elsif (not exists $def->{code} and exists $def->{output}) {
                my $new_def = $self->_compile_def($def);
                $defs->{$key} = $new_def;
            }
            $defs->{$key}->{class} ||= 'inline';
            $defs->{$key}->{classic} = 1 unless defined $defs->{$key}->{classic};
            $defs->{$key}->{close} = 1 unless defined $defs->{$key}->{close};
        }
        $self->set_compiled(1);
    }
}

sub _compile_def {
    my ($self, $def) = @_;
    my $esc = $self->get_escapes;
    my $parse = 0;
    my $new_def = {};
    my $output = $def;
    my $close = 1;
    my $class = 'inline';
    if (ref $def eq 'HASH') {
        $new_def = { %$def };
        $output = delete $new_def->{output};
        $parse = $new_def->{parse};
        $close = $new_def->{close} if exists $new_def->{close};
        $class = $new_def->{class} if exists $new_def->{class};
    }
    else {
    }
    # we have a string, compile
    #warn __PACKAGE__.':'.__LINE__.": $key => $output\n";
    if ($output =~ s/^(inline|block|url)://) {
        $class = $1;
    }
    my @parts = split m!($re_split)!, $output;
    #warn __PACKAGE__.':'.__LINE__.$".Data::Dumper->Dump([\@parts], ['parts']);
    my @compiled;
    for my $p (@parts) {
        if ($p =~ m/$re_cmp/) {
            my ($escape, $type) = ($1, $2);
            $escape ||= 'parse';
            my @escapes = split /\|/, $escape;
            if (grep { $_ eq 'parse' } @escapes) {
                $parse = 1;
            }
            push @compiled, [\@escapes, $type];
        }
        else {
            push @compiled, $p;
        }
        #warn __PACKAGE__.':'.__LINE__.$".Data::Dumper->Dump([\@compiled], ['compiled']);
    }
    my $code = sub {
        my ($self, $attr, $string, $fallback, $tag) = @_;
        my $out = '';
        for my $c (@compiled) {

            # just text
            unless (ref $c) {
                $out .= $c;
            }
            # tag attribute or content
            else {
                my ($escapes, $type) = @$c;
                my @escapes = @$escapes;
                my $var = '';
                my $attributes = $tag->get_attr;
                if ($type eq 'attr' and @$attributes > 1) {
                    my $name = shift @escapes;
                    for my $item (@$attributes[1 .. $#$attributes]) {
                        if ($item->[0] eq $name) {
                            $var = $item->[1];
                            last;
                        }
                    }
                }
                elsif ($type eq 'a') {
                    $var = $attr;
                }
                elsif ($type eq 'A') {
                    $var = $fallback;
                }
                elsif ($type eq 's') {
                    if (ref $string eq 'SCALAR') {
                        # this text is already finished and escaped
                        $string = $$string;
                    }
                    $var = $string;
                }
                for my $e (@escapes) {
                    my $sub = $esc->{$e};
                    if ($sub) {
                        $var = $sub->($self, $c, $var);
                        unless (defined $var) {
                            # if escape returns undef, we return it unparsed

lib/Parse/BBCode.pm  view on Meta::CPAN

                my $parse = $def->{parse};
                if ($parse) {
                    $o->add_content($tag);
                }
                else {
                    my $content = $tag->get_content;
                    my $string = '';
                    for my $c (@$content) {
                        if (ref $c) {
                            $string .= $c->raw_text( auto_close => 0 );
                        }
                        else {
                            $string .= $c;
                        }
                    }
                    $tag->set_content([$string]);
                    $o->add_content($tag);
                }
            }
            else {
                $o->add_content($tag);
            }
        }
        elsif (ref $tag) {
            my $def = $defs->{lc $tag->get_name};
            my $parse = $def->{parse};
            if ($parse) {
                push @tags, $tag;
            }
            else {
                my $content = $tag->get_content;
                my $string = '';
                for my $c (@$content) {
                    if (ref $c) {
                        $string .= $c->raw_text( auto_close => 0 );
                    }
                    else {
                        $string .= $c;
                    }
                }
                $tag->set_content([$string]);
                push @tags, $tag;
            }
        }
        else {
            push @tags, $tag;
        }
        $current_open_re = join '|', map {
            quotemeta $_->get_name
        } @opened;

    };
    my @class = 'block';
    while (defined $text and length $text) {
        $in_url = grep { $_->get_class eq 'url' } @opened;
        #warn __PACKAGE__.':'.__LINE__.$".Data::Dumper->Dump([\$in_url], ['in_url']);
        #warn __PACKAGE__.':'.__LINE__.": ============= match $text\n";
        my $tag;
        my ($before, $tag1, $tag2, $after);
        if ($re_classic and $re_short) {
            ($before, $tag1, $tag2, $after) = split m{
                (?:
                    \[ ($re_short)   (?=://)
                    |
                    \[ ($re_classic) (?=\b|\]|\=)
                )
            }ix, $text, 2;
        }
        elsif (! $re_classic and $re_short) {
            ($before, $tag1, $after) = split m{
                    \[ ($re_short)   (?=://)
            }ix, $text, 2;
        }
        elsif ($re_classic and !$re_short) {
            ($before, $tag2, $after) = split m{
                    \[ ($re_classic) (?=\b|\]|\=)
            }ix, $text, 2;
        }
        { no warnings;
#            warn __PACKAGE__.':'.__LINE__.": $before, $tag1, $tag2, $after)\n";
        #warn __PACKAGE__.':'.__LINE__.": RE: $current_open_re\n";
        }
        #warn __PACKAGE__.':'.__LINE__.$".Data::Dumper->Dump([\@opened], ['opened']);
        if (length $before) {
            # look if it contains a closing tag
            #warn __PACKAGE__.':'.__LINE__.": BEFORE $before\n";
            while (length $current_open_re and $before =~ s# (.*?) (\[ / ($current_open_re) \]) ##ixs) {
                # found closing tag
                my ($content, $end, $name) = ($1, $2, $3);
                #warn __PACKAGE__.':'.__LINE__.": found closing tag $name!\n";
                my $f;
                # try to find the matching opening tag
                my @not_close;
                while (@opened) {
                    my $try = pop @opened;
                    $current_open_re = join '|', map {
                        quotemeta $_->get_name
                    } @opened;
                    if ($try->get_name eq lc $name) {
                        $f = $try;
                        last;
                    }
                    elsif (!$try->get_close) {
                        $self->_finish_tag($try, '');
                        unshift @not_close, $try;
                    }
                    else {
                        # unbalanced
                        $self->_add_error('unclosed', $try);
                        if ($self->get_close_open_tags) {
                            # close
                            $f = $try;
                            unshift @not_close, $try;
                            if (@opened) {
                                $opened[-1]->add_content('');
                            }
                            $self->_finish_tag($try, '[/'. $try->get_name() .']', 1);
                        }
                        else {
                            # just add unparsed text
                            $callback_found_tag->($_) for $try->_reduce;
                        }
                    }
                }
                if (@not_close) {
                    $not_close[-1]->add_content($content);
                }
                for my $n (@not_close) {
                    $f->add_content($n);
                    #$callback_found_tag->($n);
                }
                # add text before closing tag as content to the current open tag
                if ($f) {
                    unless (@not_close) {
                        #warn __PACKAGE__.':'.__LINE__.$".Data::Dumper->Dump([\$f], ['f']);



( run in 1.326 second using v1.01-cache-2.11-cpan-71847e10f99 )