~ubuntu-branches/ubuntu/trusty/libtext-markdown-perl/trusty

« back to all changes in this revision

Viewing changes to lib/Text/Markdown.pm

  • Committer: Bazaar Package Importer
  • Author(s): gregor herrmann
  • Date: 2008-04-26 00:35:34 UTC
  • Revision ID: james.westby@ubuntu.com-20080426003534-oo979u4lubd1ltc1
Tags: upstream-1.0.19
ImportĀ upstreamĀ versionĀ 1.0.19

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
package Text::Markdown;
 
2
require 5.008_000;
 
3
use strict;
 
4
use warnings;
 
5
 
 
6
use Digest::MD5 qw(md5_hex);
 
7
use Encode      qw();
 
8
use Carp        qw(croak);
 
9
use base        'Exporter';
 
10
 
 
11
our $VERSION   = '1.0.19';
 
12
our @EXPORT_OK = qw(markdown);
 
13
 
 
14
=head1 NAME
 
15
 
 
16
Text::Markdown - Convert Markdown syntax to (X)HTML
 
17
 
 
18
=head1 SYNOPSIS
 
19
 
 
20
    use Text::Markdown 'markdown';
 
21
    my $html = markdown($text);
 
22
 
 
23
    use Text::Markdown 'markdown';
 
24
    my $html = markdown( $text, {
 
25
        empty_element_suffix => '>',
 
26
        tab_width => 2,
 
27
    } );
 
28
 
 
29
    use Text::Markdown;
 
30
    my $m = Text::Markdown->new;
 
31
    my $html = $m->markdown($text);
 
32
 
 
33
    use Text::Markdown;
 
34
    my $m = Text::MultiMarkdown->new(
 
35
        empty_element_suffix => '>',
 
36
        tab_width => 2,
 
37
    );
 
38
    my $html = $m->markdown( $text );
 
39
 
 
40
=head1 DESCRIPTION
 
41
 
 
42
Markdown is a text-to-HTML filter; it translates an easy-to-read /
 
43
easy-to-write structured text format into HTML. Markdown's text format
 
44
is most similar to that of plain text email, and supports features such
 
45
as headers, *emphasis*, code blocks, blockquotes, and links.
 
46
 
 
47
Markdown's syntax is designed not as a generic markup language, but
 
48
specifically to serve as a front-end to (X)HTML. You can use span-level
 
49
HTML tags anywhere in a Markdown document, and you can use block level
 
50
HTML tags (like <div> and <table> as well).
 
51
 
 
52
=head1 SYNTAX
 
53
 
 
54
This module implements the 'original' Markdown markdown syntax from:
 
55
 
 
56
    http://daringfireball.net/projects/markdown/
 
57
 
 
58
=head1 OPTIONS
 
59
 
 
60
Text::Markdown supports a number of options to it's processor which control the behavior of the output document.
 
61
 
 
62
These options can be supplied to the constructor, on in a hash with the individual calls to the markdown method.
 
63
See the synopsis for examples of both of the above styles.
 
64
 
 
65
The options for the processor are:
 
66
 
 
67
=over
 
68
 
 
69
=item empty element suffix
 
70
 
 
71
This option can be used to generate normal HTML output. By default, it is ' />', which is xHTML, change to '>' for normal HTML.
 
72
 
 
73
=item tab_width
 
74
 
 
75
Controls indent width in the generated markup, defaults to 4
 
76
 
 
77
=item markdown_in_html_blocks
 
78
 
 
79
Controls if Markdown is processed when inside HTML blocks. Defaults to 0.
 
80
 
 
81
=back
 
82
 
 
83
=head1 METHODS
 
84
 
 
85
=cut
 
86
 
 
87
# Regex to match balanced [brackets]. See Friedl's
 
88
# "Mastering Regular Expressions", 2nd Ed., pp. 328-331.
 
89
our ($g_nested_brackets, $g_nested_parens); 
 
90
$g_nested_brackets = qr{
 
91
    (?>                                 # Atomic matching
 
92
       [^\[\]]+                         # Anything other than brackets
 
93
     | 
 
94
       \[
 
95
         (??{ $g_nested_brackets })     # Recursive set of nested brackets
 
96
       \]
 
97
    )*
 
98
}x;
 
99
# Doesn't allow for whitespace, because we're using it to match URLs:
 
100
$g_nested_parens = qr{
 
101
        (?>                                                             # Atomic matching
 
102
           [^()\s]+                                                     # Anything other than parens or whitespace
 
103
         | 
 
104
           \(
 
105
                 (??{ $g_nested_parens })               # Recursive set of nested brackets
 
106
           \)
 
107
        )*
 
108
}x;
 
109
 
 
110
# Table of hash values for escaped characters:
 
111
our %g_escape_table;
 
112
foreach my $char (split //, '\\`*_{}[]()>#+-.!') {
 
113
    $g_escape_table{$char} = md5_hex($char);
 
114
}
 
115
 
 
116
=head1 METHODS
 
117
 
 
118
=head2 new
 
119
 
 
120
A simple constructor, see the SYNTAX and OPTIONS sections for more information.
 
121
 
 
122
=cut
 
123
 
 
124
sub new {
 
125
    my ($class, %p) = @_;
 
126
    
 
127
    $p{base_url} ||= ''; # This is the base url to be used for WikiLinks
 
128
    
 
129
    $p{tab_width} = 4 unless (defined $p{tab_width} and $p{tab_width} =~ m/^\d+$/);
 
130
    
 
131
    $p{empty_element_suffix} ||= ' />'; # Change to ">" for HTML output
 
132
        
 
133
    # Is markdown processed in HTML blocks? See t/15inlinehtmldonotturnoffmarkdown.t
 
134
    $p{markdown_in_html_blocks} = $p{markdown_in_html_blocks} ? 1 : 0;
 
135
    
 
136
    my $self = { params => \%p };
 
137
    bless $self, ref($class) || $class;
 
138
    return $self;
 
139
}
 
140
 
 
141
=head2 markdown
 
142
 
 
143
The main function as far as the outside world is concerned. See the SYNOPSIS
 
144
for details on use.
 
145
 
 
146
=cut
 
147
 
 
148
sub markdown {
 
149
    my ( $self, $text, $options ) = @_;
 
150
 
 
151
    # Detect functional mode, and create an instance for this run..
 
152
    unless (ref $self) {
 
153
        if ( $self ne __PACKAGE__ ) {
 
154
            my $ob = __PACKAGE__->new();
 
155
                                # $self is text, $text is options
 
156
            return $ob->markdown($self, $text);
 
157
        }
 
158
        else {
 
159
            croak('Calling ' . $self . '->markdown (as a class method) is not supported.');
 
160
        }
 
161
    }
 
162
 
 
163
    $options ||= {};
 
164
 
 
165
    %$self = (%{ $self->{params} }, %$options, params => $self->{params});
 
166
 
 
167
    $self->_CleanUpRunData($options);
 
168
    
 
169
    return $self->_Markdown($text);
 
170
}
 
171
 
 
172
sub _CleanUpRunData {
 
173
    my ($self, $options) = @_;
 
174
    # Clear the global hashes. If we don't clear these, you get conflicts
 
175
    # from other articles when generating a page which contains more than
 
176
    # one article (e.g. an index page that shows the N most recent
 
177
    # articles):
 
178
    $self->{_urls}        = $options->{urls} ? $options->{urls} : {}; # FIXME - document passing this option (tested in 05options.t).
 
179
    $self->{_titles}      = {};
 
180
    $self->{_html_blocks} = {};
 
181
    # Used to track when we're inside an ordered or unordered list
 
182
    # (see _ProcessListItems() for details)
 
183
    $self->{_list_level} = 0;
 
184
 
 
185
}
 
186
 
 
187
sub _Markdown {
 
188
#
 
189
# Main function. The order in which other subs are called here is
 
190
# essential. Link and image substitutions need to happen before
 
191
# _EscapeSpecialChars(), so that any *'s or _'s in the <a>
 
192
# and <img> tags get encoded.
 
193
#
 
194
    my ($self, $text, $options) = @_;
 
195
 
 
196
    $text = $self->_CleanUpDoc($text);
 
197
    
 
198
    # Turn block-level HTML blocks into hash entries
 
199
    $text = $self->_HashHTMLBlocks($text) unless $self->{markdown_in_html_blocks};
 
200
 
 
201
    $text = $self->_StripLinkDefinitions($text);
 
202
    
 
203
    $text = $self->_RunBlockGamut($text);
 
204
    
 
205
    $text = $self->_UnescapeSpecialChars($text);
 
206
        
 
207
    $text = $self->_ConvertCopyright($text);
 
208
 
 
209
    return $text . "\n";
 
210
}
 
211
 
 
212
=head2 urls
 
213
 
 
214
Returns a reference to a hash with the key being the markdown reference and the value being the URL.
 
215
 
 
216
Useful for building scripts which preprocess a list of links before the main content. See t/05options.t
 
217
for an example of this hashref being passed back into the markdown method to create links.
 
218
 
 
219
=cut
 
220
 
 
221
sub urls {
 
222
    my ( $self ) = @_;
 
223
    
 
224
    return $self->{_urls};
 
225
}
 
226
 
 
227
sub _CleanUpDoc {
 
228
    my ($self, $text) = @_;
 
229
    
 
230
    # Standardize line endings:
 
231
    $text =~ s{\r\n}{\n}g;  # DOS to Unix
 
232
    $text =~ s{\r}{\n}g;    # Mac to Unix
 
233
 
 
234
    # Make sure $text ends with a couple of newlines:
 
235
    $text .= "\n\n";
 
236
 
 
237
    # Convert all tabs to spaces.
 
238
    $text = $self->_Detab($text);
 
239
 
 
240
    # Strip any lines consisting only of spaces and tabs.
 
241
    # This makes subsequent regexen easier to write, because we can
 
242
    # match consecutive blank lines with /\n+/ instead of something
 
243
    # contorted like /[ \t]*\n+/ .
 
244
    $text =~ s/^[ \t]+$//mg;
 
245
    
 
246
    return $text;
 
247
}
 
248
 
 
249
sub _StripLinkDefinitions {
 
250
#
 
251
# Strips link definitions from text, stores the URLs and titles in
 
252
# hash references.
 
253
#
 
254
    my ($self, $text) = @_;
 
255
    my $less_than_tab = $self->{tab_width} - 1;
 
256
 
 
257
    # Link defs are in the form: ^[id]: url "optional title"
 
258
    while ($text =~ s{
 
259
            ^[ ]{0,$less_than_tab}\[(.+)\]: # id = \$1
 
260
              [ \t]*
 
261
              \n?               # maybe *one* newline
 
262
              [ \t]*
 
263
            <?(\S+?)>?          # url = \$2
 
264
              [ \t]*
 
265
              \n?               # maybe one newline
 
266
              [ \t]*
 
267
            (?:
 
268
                (?<=\s)         # lookbehind for whitespace
 
269
                ["(]
 
270
                (.+?)           # title = \$3
 
271
                [")]
 
272
                [ \t]*
 
273
            )?  # title is optional
 
274
            (?:\n+|\Z)
 
275
        }{}omx) {
 
276
        $self->{_urls}{lc $1} = $self->_EncodeAmpsAndAngles( $2 );    # Link IDs are case-insensitive
 
277
        if ($3) {
 
278
            $self->{_titles}{lc $1} = $3;
 
279
            $self->{_titles}{lc $1} =~ s/"/&quot;/g;
 
280
        }
 
281
        
 
282
    }
 
283
 
 
284
    return $text;
 
285
}
 
286
 
 
287
sub _md5_utf8 {
 
288
   # Internal function used to safely MD5sum chunks of the input, which might be Unicode in Perl's internal representation.
 
289
   my $input = shift;
 
290
   return unless defined $input;
 
291
   if (Encode::is_utf8 $input) {
 
292
       return md5_hex(Encode::encode('utf8', $input));
 
293
    } 
 
294
    else {
 
295
        return md5_hex($input);
 
296
    }
 
297
}
 
298
 
 
299
sub _HashHTMLBlocks {
 
300
    my ($self, $text) = @_;
 
301
    my $less_than_tab = $self->{tab_width} - 1;
 
302
 
 
303
        # Hashify HTML blocks:
 
304
        # We only want to do this for block-level HTML tags, such as headers,
 
305
        # lists, and tables. That's because we still want to wrap <p>s around
 
306
        # "paragraphs" that are wrapped in non-block-level tags, such as anchors,
 
307
        # phrase emphasis, and spans. The list of tags we're looking for is
 
308
        # hard-coded:
 
309
        my $block_tags = qr{
 
310
                  (?:
 
311
                        p         |  div     |  h[1-6]  |  blockquote  |  pre       |  table  |
 
312
                        dl        |  ol      |  ul      |  script      |  noscript  |  form   |
 
313
                        fieldset  |  iframe  |  math    |  ins         |  del
 
314
                  )
 
315
                }x;
 
316
 
 
317
        my $tag_attrs = qr{
 
318
                                                (?:                             # Match one attr name/value pair
 
319
                                                        \s+                             # There needs to be at least some whitespace
 
320
                                                                                        # before each attribute name.
 
321
                                                        [\w.:_-]+               # Attribute name
 
322
                                                        \s*=\s*
 
323
                                                        (?:
 
324
                                                                ".+?"           # "Attribute value"
 
325
                                                         |
 
326
                                                                '.+?'           # 'Attribute value'
 
327
                                                        )
 
328
                                                )*                              # Zero or more
 
329
                                        }x;
 
330
 
 
331
        my $empty_tag = qr{< \w+ $tag_attrs \s* />}oxms;
 
332
        my $open_tag =  qr{< $block_tags $tag_attrs \s* >}oxms;
 
333
        my $close_tag = undef;  # let Text::Balanced handle this
 
334
 
 
335
        use Text::Balanced qw(gen_extract_tagged);
 
336
        my $extract_block = gen_extract_tagged($open_tag, $close_tag, undef, { ignore => [$empty_tag] });
 
337
 
 
338
        my @chunks;
 
339
        while ($text =~ s{^(([ ]{0,$less_than_tab}<)?.*\n)}{}m) {
 
340
                my $cur_line = $1;
 
341
                if (defined $2) {
 
342
                        # current line could be start of code block
 
343
 
 
344
                        my ($tag, $remainder) = $extract_block->($cur_line . $text);
 
345
                        if ($tag) {
 
346
                                my $key = _md5_utf8($tag);
 
347
                                $self->{_html_blocks}{$key} = $tag;
 
348
                                push @chunks, "\n\n" . $key . "\n\n";
 
349
                                $text = $remainder;
 
350
                        }
 
351
                        else {
 
352
                                # No tag match, so toss $cur_line into @chunks
 
353
                                push @chunks, $cur_line;
 
354
                        }
 
355
                }
 
356
                else {
 
357
                        # current line could NOT be start of code block
 
358
                        push @chunks, $cur_line;
 
359
                }
 
360
 
 
361
        }
 
362
        push @chunks, $text; # Whatever is left.
 
363
 
 
364
        $text = join '', @chunks;
 
365
 
 
366
        # Special case just for <hr />. It was easier to make a special case than
 
367
        # to make the other regex more complicated.     
 
368
        $text = $self->_HashHR($text);
 
369
        
 
370
    $text = $self->_HashHTMLComments($text);
 
371
 
 
372
    $text = $self->_HashPHPASPBlocks($text);
 
373
 
 
374
        return $text;
 
375
}
 
376
 
 
377
sub _HashHR {
 
378
    my ($self, $text) = @_;
 
379
    my $less_than_tab = $self->{tab_width} - 1;
 
380
    
 
381
        $text =~ s{
 
382
                                (?:
 
383
                                        (?<=\n\n)               # Starting after a blank line
 
384
                                        |                               # or
 
385
                                        \A\n?                   # the beginning of the doc
 
386
                                )
 
387
                                (                                               # save in $1
 
388
                                        [ ]{0,$less_than_tab}
 
389
                                        <(hr)                           # start tag = $2
 
390
                                        \b                                      # word break
 
391
                                        ([^<>])*?                       # 
 
392
                                        /?>                                     # the matching end tag
 
393
                                        [ \t]*
 
394
                                        (?=\n{2,}|\Z)           # followed by a blank line or end of document
 
395
                                )
 
396
        }{
 
397
                my $key = _md5_utf8($1);
 
398
                $self->{_html_blocks}{$key} = $1;
 
399
                "\n\n" . $key . "\n\n";
 
400
        }egx;
 
401
                        
 
402
        return $text;
 
403
}
 
404
 
 
405
sub _HashHTMLComments {
 
406
    my ($self, $text) = @_;
 
407
    my $less_than_tab = $self->{tab_width} - 1;
 
408
    
 
409
    # Special case for standalone HTML comments:
 
410
        $text =~ s{
 
411
                                (?:
 
412
                                        (?<=\n\n)               # Starting after a blank line
 
413
                                        |                               # or
 
414
                                        \A\n?                   # the beginning of the doc
 
415
                                )
 
416
                                (                                               # save in $1
 
417
                                        [ ]{0,$less_than_tab}
 
418
                                        (?s:
 
419
                                                <!
 
420
                                                (--.*?--\s*)+
 
421
                                                >
 
422
                                        )
 
423
                                        [ \t]*
 
424
                                        (?=\n{2,}|\Z)           # followed by a blank line or end of document
 
425
                                )
 
426
        }{
 
427
                my $key = _md5_utf8($1);
 
428
                $self->{_html_blocks}{$key} = $1;
 
429
                "\n\n" . $key . "\n\n";
 
430
        }egx;
 
431
        
 
432
        return $text;
 
433
}
 
434
 
 
435
sub _HashPHPASPBlocks {
 
436
    my ($self, $text) = @_;
 
437
    my $less_than_tab = $self->{tab_width} - 1;
 
438
    
 
439
    # PHP and ASP-style processor instructions (<?ā€¦?> and <%ā€¦%>)
 
440
        $text =~ s{
 
441
                                (?:
 
442
                                        (?<=\n\n)               # Starting after a blank line
 
443
                                        |                               # or
 
444
                                        \A\n?                   # the beginning of the doc
 
445
                                )
 
446
                                (                                               # save in $1
 
447
                                        [ ]{0,$less_than_tab}
 
448
                                        (?s:
 
449
                                                <([?%])                 # $2
 
450
                                                .*?
 
451
                                                \2>
 
452
                                        )
 
453
                                        [ \t]*
 
454
                                        (?=\n{2,}|\Z)           # followed by a blank line or end of document
 
455
                                )
 
456
                        }{
 
457
                                my $key = _md5_utf8($1);
 
458
                                $self->{_html_blocks}{$key} = $1;
 
459
                                "\n\n" . $key . "\n\n";
 
460
                        }egx;
 
461
        return $text;
 
462
}
 
463
 
 
464
sub _RunBlockGamut {
 
465
#
 
466
# These are all the transformations that form block-level
 
467
# tags like paragraphs, headers, and list items.
 
468
#
 
469
    my ($self, $text) = @_;
 
470
 
 
471
    # Do headers first, as these populate cross-refs
 
472
    $text = $self->_DoHeaders($text);
 
473
    
 
474
    # And now, protect our tables
 
475
    $text = $self->_HashHTMLBlocks($text) unless $self->{markdown_in_html_blocks};
 
476
 
 
477
    # Do Horizontal Rules:
 
478
    $text =~ s{^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$}{\n<hr$self->{empty_element_suffix}\n}gmx;
 
479
    $text =~ s{^[ ]{0,2}([ ]? -[ ]?){3,}[ \t]*$}{\n<hr$self->{empty_element_suffix}\n}gmx;
 
480
    $text =~ s{^[ ]{0,2}([ ]? _[ ]?){3,}[ \t]*$}{\n<hr$self->{empty_element_suffix}\n}gmx;
 
481
 
 
482
    $text = $self->_DoLists($text);
 
483
 
 
484
    $text = $self->_DoCodeBlocks($text);
 
485
 
 
486
    $text = $self->_DoBlockQuotes($text);
 
487
 
 
488
    # We already ran _HashHTMLBlocks() before, in Markdown(), but that
 
489
    # was to escape raw HTML in the original Markdown source. This time,
 
490
    # we're escaping the markup we've just created, so that we don't wrap
 
491
    # <p> tags around block-level tags.
 
492
    $text = $self->_HashHTMLBlocks($text);
 
493
 
 
494
    $text = $self->_FormParagraphs($text);
 
495
 
 
496
    return $text;
 
497
}
 
498
 
 
499
sub _RunSpanGamut {
 
500
#
 
501
# These are all the transformations that occur *within* block-level
 
502
# tags like paragraphs, headers, and list items.
 
503
#
 
504
    my ($self, $text) = @_;
 
505
 
 
506
    $text = $self->_DoCodeSpans($text);
 
507
        $text = $self->_EscapeSpecialCharsWithinTagAttributes($text);
 
508
    $text = $self->_EscapeSpecialChars($text);
 
509
 
 
510
    # Process anchor and image tags. Images must come first,
 
511
    # because ![foo][f] looks like an anchor.
 
512
    $text = $self->_DoImages($text);
 
513
    $text = $self->_DoAnchors($text);
 
514
 
 
515
    # Make links out of things like `<http://example.com/>`
 
516
    # Must come after _DoAnchors(), because you can use < and >
 
517
    # delimiters in inline links like [this](<url>).
 
518
    $text = $self->_DoAutoLinks($text);
 
519
 
 
520
    $text = $self->_EncodeAmpsAndAngles($text);
 
521
 
 
522
    $text = $self->_DoItalicsAndBold($text);
 
523
 
 
524
    # FIXME - Is hard coding space here sane, or does this want to be related to tab width?
 
525
    # Do hard breaks:
 
526
    $text =~ s/ {2,}\n/ <br$self->{empty_element_suffix}\n/g;
 
527
 
 
528
    return $text;
 
529
}
 
530
 
 
531
sub _EscapeSpecialChars {
 
532
    my ($self, $text) = @_;
 
533
    my $tokens ||= $self->_TokenizeHTML($text);
 
534
 
 
535
    $text = '';   # rebuild $text from the tokens
 
536
#   my $in_pre = 0;  # Keep track of when we're inside <pre> or <code> tags.
 
537
#   my $tags_to_skip = qr!<(/?)(?:pre|code|kbd|script|math)[\s>]!;
 
538
 
 
539
    foreach my $cur_token (@$tokens) {
 
540
        if ($cur_token->[0] eq "tag") {
 
541
            # Within tags, encode * and _ so they don't conflict
 
542
            # with their use in Markdown for italics and strong.
 
543
            # We're replacing each such character with its
 
544
            # corresponding MD5 checksum value; this is likely
 
545
            # overkill, but it should prevent us from colliding
 
546
            # with the escape values by accident.
 
547
            $cur_token->[1] =~  s! \* !$g_escape_table{'*'}!ogx;
 
548
            $cur_token->[1] =~  s! _  !$g_escape_table{'_'}!ogx;
 
549
            $text .= $cur_token->[1];
 
550
        } else {
 
551
            my $t = $cur_token->[1];
 
552
            $t = $self->_EncodeBackslashEscapes($t);
 
553
            $text .= $t;
 
554
        }
 
555
    }
 
556
    return $text;
 
557
}
 
558
 
 
559
sub _EscapeSpecialCharsWithinTagAttributes {
 
560
#
 
561
# Within tags -- meaning between < and > -- encode [\ ` * _] so they
 
562
# don't conflict with their use in Markdown for code, italics and strong.
 
563
# We're replacing each such character with its corresponding MD5 checksum
 
564
# value; this is likely overkill, but it should prevent us from colliding
 
565
# with the escape values by accident.
 
566
#
 
567
        my ($self, $text) = @_;
 
568
        my $tokens ||= $self->_TokenizeHTML($text);
 
569
        $text = '';   # rebuild $text from the tokens
 
570
 
 
571
        foreach my $cur_token (@$tokens) {
 
572
                if ($cur_token->[0] eq "tag") {
 
573
                        $cur_token->[1] =~  s! \\ !$g_escape_table{'\\'}!gox;
 
574
                        $cur_token->[1] =~  s{ (?<=.)</?code>(?=.)  }{$g_escape_table{'`'}}gox;
 
575
                        $cur_token->[1] =~  s! \* !$g_escape_table{'*'}!gox;
 
576
                        $cur_token->[1] =~  s! _  !$g_escape_table{'_'}!gox;
 
577
                }
 
578
                $text .= $cur_token->[1];
 
579
        }
 
580
        return $text;
 
581
}
 
582
 
 
583
sub _DoAnchors {
 
584
#
 
585
# Turn Markdown link shortcuts into XHTML <a> tags.
 
586
#
 
587
    my ($self, $text) = @_;
 
588
 
 
589
    #
 
590
    # First, handle reference-style links: [link text] [id]
 
591
    #
 
592
    $text =~ s{
 
593
        (                   # wrap whole match in $1
 
594
          \[
 
595
            ($g_nested_brackets)    # link text = $2
 
596
          \]
 
597
 
 
598
          [ ]?              # one optional space
 
599
          (?:\n[ ]*)?       # one optional newline followed by spaces
 
600
 
 
601
          \[
 
602
            (.*?)       # id = $3
 
603
          \]
 
604
        )
 
605
    }{
 
606
        my $whole_match = $1;
 
607
        my $link_text   = $2;
 
608
        my $link_id     = lc $3;
 
609
 
 
610
        if ($link_id eq "") {
 
611
            $link_id = lc $link_text;   # for shortcut links like [this][].
 
612
        }
 
613
        
 
614
        $link_id =~ s{[ ]*\n}{ }g; # turn embedded newlines into spaces
 
615
        
 
616
        $self->_GenerateAnchor($whole_match, $link_text, $link_id);
 
617
    }xsge;
 
618
 
 
619
    #
 
620
    # Next, inline-style links: [link text](url "optional title")
 
621
    #
 
622
    $text =~ s{
 
623
        (               # wrap whole match in $1
 
624
          \[
 
625
            ($g_nested_brackets)    # link text = $2
 
626
          \]
 
627
          \(            # literal paren
 
628
            [ \t]*
 
629
            ($g_nested_parens)   # href = $3
 
630
            [ \t]*
 
631
            (           # $4
 
632
              (['"])    # quote char = $5
 
633
              (.*?)     # Title = $6
 
634
              \5        # matching quote
 
635
              [ \t]*    # ignore any spaces/tabs between closing quote and )
 
636
            )?          # title is optional
 
637
          \)
 
638
        )
 
639
    }{
 
640
        my $result;
 
641
        my $whole_match = $1;
 
642
        my $link_text   = $2;
 
643
        my $url         = $3;
 
644
        my $title       = $6;
 
645
        
 
646
        $self->_GenerateAnchor($whole_match, $link_text, undef, $url, $title);
 
647
    }xsge;
 
648
    
 
649
    #
 
650
        # Last, handle reference-style shortcuts: [link text]
 
651
        # These must come last in case you've also got [link test][1]
 
652
        # or [link test](/foo)
 
653
        #
 
654
        $text =~ s{
 
655
                (                                       # wrap whole match in $1
 
656
                  \[
 
657
                    ([^\[\]]+)          # link text = $2; can't contain '[' or ']'
 
658
                  \]
 
659
                )
 
660
        }{
 
661
                my $result;
 
662
                my $whole_match = $1;
 
663
                my $link_text   = $2;
 
664
                (my $link_id = lc $2) =~ s{[ ]*\n}{ }g; # lower-case and turn embedded newlines into spaces
 
665
 
 
666
        $self->_GenerateAnchor($whole_match, $link_text, $link_id);
 
667
        }xsge;
 
668
 
 
669
    return $text;
 
670
}
 
671
 
 
672
sub _GenerateAnchor {
 
673
    # FIXME - Fugly, change to named params?
 
674
    my ($self, $whole_match, $link_text, $link_id, $url, $title, $attributes) = @_;
 
675
    
 
676
    my $result;
 
677
    
 
678
    $attributes = '' unless defined $attributes;
 
679
    
 
680
    if ( !defined $url && defined $self->{_urls}{$link_id}) {
 
681
        $url = $self->{_urls}{$link_id};
 
682
    }
 
683
    
 
684
    if (!defined $url) {
 
685
        return $whole_match;
 
686
    }
 
687
        
 
688
    $url =~ s! \* !$g_escape_table{'*'}!gox;     # We've got to encode these to avoid
 
689
    $url =~ s!  _ !$g_escape_table{'_'}!gox;     # conflicting with italics/bold.
 
690
    $url =~ s{^<(.*)>$}{$1};                                    # Remove <>'s surrounding URL, if present
 
691
        
 
692
    $result = qq{<a href="$url"};
 
693
        
 
694
    if ( !defined $title && defined $link_id && defined $self->{_titles}{$link_id} ) {
 
695
        $title = $self->{_titles}{$link_id};
 
696
    }
 
697
    
 
698
    if ( defined $title ) {
 
699
        $title =~ s/"/&quot;/g;
 
700
        $title =~ s! \* !$g_escape_table{'*'}!gox;
 
701
        $title =~ s!  _ !$g_escape_table{'_'}!gox;
 
702
        $result .=  qq{ title="$title"};
 
703
    }
 
704
    
 
705
    $result .= "$attributes>$link_text</a>";
 
706
 
 
707
    return $result;
 
708
}
 
709
 
 
710
sub _DoImages {
 
711
#
 
712
# Turn Markdown image shortcuts into <img> tags.
 
713
#
 
714
    my ($self, $text) = @_;
 
715
 
 
716
    #
 
717
    # First, handle reference-style labeled images: ![alt text][id]
 
718
    #
 
719
    $text =~ s{
 
720
        (               # wrap whole match in $1
 
721
          !\[
 
722
            (.*?)       # alt text = $2
 
723
          \]
 
724
 
 
725
          [ ]?              # one optional space
 
726
          (?:\n[ ]*)?       # one optional newline followed by spaces
 
727
 
 
728
          \[
 
729
            (.*?)       # id = $3
 
730
          \]
 
731
 
 
732
        )
 
733
    }{
 
734
        my $result;
 
735
        my $whole_match = $1;
 
736
        my $alt_text    = $2;
 
737
        my $link_id     = lc $3;
 
738
        
 
739
        if ($link_id eq '') {
 
740
            $link_id = lc $alt_text;     # for shortcut links like ![this][].
 
741
        }
 
742
        
 
743
        $self->_GenerateImage($whole_match, $alt_text, $link_id);
 
744
    }xsge;
 
745
 
 
746
    #
 
747
    # Next, handle inline images:  ![alt text](url "optional title")
 
748
    # Don't forget: encode * and _
 
749
 
 
750
    $text =~ s{
 
751
        (               # wrap whole match in $1
 
752
          !\[
 
753
            (.*?)       # alt text = $2
 
754
          \]
 
755
          \(            # literal paren
 
756
            [ \t]*
 
757
            ($g_nested_parens)  # src url - href = $3
 
758
            [ \t]*
 
759
            (           # $4
 
760
              (['"])    # quote char = $5
 
761
              (.*?)     # title = $6
 
762
              \5        # matching quote
 
763
              [ \t]*
 
764
            )?          # title is optional
 
765
          \)
 
766
        )
 
767
    }{
 
768
        my $result;
 
769
        my $whole_match = $1;
 
770
        my $alt_text    = $2;
 
771
        my $url         = $3;
 
772
        my $title       = '';
 
773
        if (defined($6)) {
 
774
            $title      = $6;
 
775
        }
 
776
 
 
777
        $self->_GenerateImage($whole_match, $alt_text, undef, $url, $title);
 
778
    }xsge;
 
779
 
 
780
    return $text;
 
781
}
 
782
 
 
783
sub _GenerateImage {
 
784
    # FIXME - Fugly, change to named params?
 
785
    my ($self, $whole_match, $alt_text, $link_id, $url, $title, $attributes) = @_;
 
786
    
 
787
    my $result;
 
788
    
 
789
    $attributes = '' unless defined $attributes;
 
790
    
 
791
    $alt_text ||= '';
 
792
    $alt_text =~ s/"/&quot;/g;
 
793
    # FIXME - how about >
 
794
    
 
795
    if ( !defined $url && defined $self->{_urls}{$link_id}) {
 
796
        $url = $self->{_urls}{$link_id};
 
797
    }
 
798
    
 
799
    # If there's no such link ID, leave intact:
 
800
    return $whole_match unless defined $url; 
 
801
    
 
802
    $url =~ s! \* !$g_escape_table{'*'}!ogx;     # We've got to encode these to avoid
 
803
    $url =~ s!  _ !$g_escape_table{'_'}!ogx;     # conflicting with italics/bold.
 
804
    $url =~ s{^<(.*)>$}{$1};                                    # Remove <>'s surrounding URL, if present
 
805
    
 
806
    if (!defined $title && length $link_id && defined $self->{_titles}{$link_id} && length $self->{_titles}{$link_id}) {
 
807
        $title = $self->{_titles}{$link_id};
 
808
    }    
 
809
 
 
810
    $result = qq{<img src="$url" alt="$alt_text"};
 
811
    if (defined $title && length $title) {
 
812
        $title =~ s! \* !$g_escape_table{'*'}!ogx;
 
813
        $title =~ s!  _ !$g_escape_table{'_'}!ogx;
 
814
        $title    =~ s/"/&quot;/g;
 
815
        $result .=  qq{ title="$title"};
 
816
    }
 
817
    $result .= $attributes . $self->{empty_element_suffix};
 
818
 
 
819
    return $result;
 
820
}
 
821
 
 
822
sub _DoHeaders {
 
823
    my ($self, $text) = @_;
 
824
    
 
825
    # Setext-style headers:
 
826
    #     Header 1
 
827
    #     ========
 
828
    #  
 
829
    #     Header 2
 
830
    #     --------
 
831
    #
 
832
    $text =~ s{ ^(.+)[ \t]*\n=+[ \t]*\n+ }{
 
833
        $self->_GenerateHeader('1', $1);
 
834
    }egmx;
 
835
 
 
836
    $text =~ s{ ^(.+)[ \t]*\n-+[ \t]*\n+ }{
 
837
        $self->_GenerateHeader('2', $1);
 
838
    }egmx;
 
839
 
 
840
 
 
841
    # atx-style headers:
 
842
    #   # Header 1
 
843
    #   ## Header 2
 
844
    #   ## Header 2 with closing hashes ##
 
845
    #   ...
 
846
    #   ###### Header 6
 
847
    #
 
848
    my $l;
 
849
    $text =~ s{
 
850
            ^(\#{1,6})  # $1 = string of #'s
 
851
            [ \t]*
 
852
            (.+?)       # $2 = Header text
 
853
            [ \t]*
 
854
            \#*         # optional closing #'s (not counted)
 
855
            \n+
 
856
        }{
 
857
            my $h_level = length($1);
 
858
            $self->_GenerateHeader($h_level, $2);
 
859
        }egmx;
 
860
 
 
861
    return $text;
 
862
}
 
863
 
 
864
sub _GenerateHeader {
 
865
    my ($self, $level, $id) = @_;
 
866
 
 
867
    return "<h$level>"  .  $self->_RunSpanGamut($id)  .  "</h$level>\n\n";
 
868
}
 
869
 
 
870
sub _DoLists {
 
871
#
 
872
# Form HTML ordered (numbered) and unordered (bulleted) lists.
 
873
#
 
874
    my ($self, $text) = @_;
 
875
    my $less_than_tab = $self->{tab_width} - 1;
 
876
 
 
877
    # Re-usable patterns to match list item bullets and number markers:
 
878
    my $marker_ul  = qr/[*+-]/;
 
879
    my $marker_ol  = qr/\d+[.]/;
 
880
    my $marker_any = qr/(?:$marker_ul|$marker_ol)/;
 
881
 
 
882
    # Re-usable pattern to match any entirel ul or ol list:
 
883
    my $whole_list = qr{
 
884
        (                               # $1 = whole list
 
885
          (                             # $2
 
886
            [ ]{0,$less_than_tab}
 
887
            (${marker_any})             # $3 = first list item marker
 
888
            [ \t]+
 
889
          )
 
890
          (?s:.+?)
 
891
          (                             # $4
 
892
              \z
 
893
            |
 
894
              \n{2,}
 
895
              (?=\S)
 
896
              (?!                       # Negative lookahead for another list item marker
 
897
                [ \t]*
 
898
                ${marker_any}[ \t]+
 
899
              )
 
900
          )
 
901
        )
 
902
    }mx;
 
903
 
 
904
    # We use a different prefix before nested lists than top-level lists.
 
905
    # See extended comment in _ProcessListItems().
 
906
    #
 
907
    # Note: There's a bit of duplication here. My original implementation
 
908
    # created a scalar regex pattern as the conditional result of the test on
 
909
    # $self->{_list_level}, and then only ran the $text =~ s{...}{...}egmx
 
910
    # substitution once, using the scalar as the pattern. This worked,
 
911
    # everywhere except when running under MT on my hosting account at Pair
 
912
    # Networks. There, this caused all rebuilds to be killed by the reaper (or
 
913
    # perhaps they crashed, but that seems incredibly unlikely given that the
 
914
    # same script on the same server ran fine *except* under MT. I've spent
 
915
    # more time trying to figure out why this is happening than I'd like to
 
916
    # admit. My only guess, backed up by the fact that this workaround works,
 
917
    # is that Perl optimizes the substition when it can figure out that the
 
918
    # pattern will never change, and when this optimization isn't on, we run
 
919
    # afoul of the reaper. Thus, the slightly redundant code to that uses two
 
920
    # static s/// patterns rather than one conditional pattern.
 
921
 
 
922
    if ($self->{_list_level}) {
 
923
        $text =~ s{
 
924
                ^
 
925
                $whole_list
 
926
            }{
 
927
                my $list = $1;
 
928
                my $list_type = ($3 =~ m/$marker_ul/) ? "ul" : "ol";
 
929
                # Turn double returns into triple returns, so that we can make a
 
930
                # paragraph for the last item in a list, if necessary:
 
931
                $list =~ s/\n{2,}/\n\n\n/g;
 
932
                my $result = ( $list_type eq 'ul' ) ?
 
933
                    $self->_ProcessListItems($list, $marker_ul)
 
934
                  : $self->_ProcessListItems($list, $marker_ol);
 
935
                $result = "<$list_type>\n" . $result . "</$list_type>\n";
 
936
                $result;
 
937
            }egmx;
 
938
    }
 
939
    else {
 
940
        $text =~ s{
 
941
                (?:(?<=\n\n)|\A\n?)
 
942
                $whole_list
 
943
            }{
 
944
                my $list = $1;
 
945
                my $list_type = ($3 =~ m/$marker_ul/) ? "ul" : "ol";
 
946
                # Turn double returns into triple returns, so that we can make a
 
947
                # paragraph for the last item in a list, if necessary:
 
948
                $list =~ s/\n{2,}/\n\n\n/g;
 
949
                my $result = ( $list_type eq 'ul' ) ?
 
950
                    $self->_ProcessListItems($list, $marker_ul)
 
951
                  : $self->_ProcessListItems($list, $marker_ol);
 
952
                $result = "<$list_type>\n" . $result . "</$list_type>\n";
 
953
                $result;
 
954
            }egmx;
 
955
    }
 
956
 
 
957
 
 
958
    return $text;
 
959
}
 
960
 
 
961
sub _ProcessListItems {
 
962
#
 
963
#   Process the contents of a single ordered or unordered list, splitting it
 
964
#   into individual list items.
 
965
#
 
966
 
 
967
    my ($self, $list_str, $marker_any) = @_;
 
968
 
 
969
 
 
970
    # The $self->{_list_level} global keeps track of when we're inside a list.
 
971
    # Each time we enter a list, we increment it; when we leave a list,
 
972
    # we decrement. If it's zero, we're not in a list anymore.
 
973
    #
 
974
    # We do this because when we're not inside a list, we want to treat
 
975
    # something like this:
 
976
    #
 
977
    #       I recommend upgrading to version
 
978
    #       8. Oops, now this line is treated
 
979
    #       as a sub-list.
 
980
    #
 
981
    # As a single paragraph, despite the fact that the second line starts
 
982
    # with a digit-period-space sequence.
 
983
    #
 
984
    # Whereas when we're inside a list (or sub-list), that line will be
 
985
    # treated as the start of a sub-list. What a kludge, huh? This is
 
986
    # an aspect of Markdown's syntax that's hard to parse perfectly
 
987
    # without resorting to mind-reading. Perhaps the solution is to
 
988
    # change the syntax rules such that sub-lists must start with a
 
989
    # starting cardinal number; e.g. "1." or "a.".
 
990
 
 
991
    $self->{_list_level}++;
 
992
 
 
993
    # trim trailing blank lines:
 
994
    $list_str =~ s/\n{2,}\z/\n/;
 
995
 
 
996
 
 
997
    $list_str =~ s{
 
998
        (\n)?                           # leading line = $1
 
999
        (^[ \t]*)                       # leading whitespace = $2
 
1000
        ($marker_any) [ \t]+            # list marker = $3
 
1001
        ((?s:.+?)                       # list item text   = $4
 
1002
        (\n{1,2}))
 
1003
        (?= \n* (\z | \2 ($marker_any) [ \t]+))
 
1004
    }{
 
1005
        my $item = $4;
 
1006
        my $leading_line = $1;
 
1007
        my $leading_space = $2;
 
1008
 
 
1009
        if ($leading_line or ($item =~ m/\n{2,}/)) {
 
1010
            $item = $self->_RunBlockGamut($self->_Outdent($item));
 
1011
        }
 
1012
        else {
 
1013
            # Recursion for sub-lists:
 
1014
            $item = $self->_DoLists($self->_Outdent($item));
 
1015
            chomp $item;
 
1016
            $item = $self->_RunSpanGamut($item);
 
1017
        }
 
1018
 
 
1019
        "<li>" . $item . "</li>\n";
 
1020
    }egmx;
 
1021
 
 
1022
    $self->{_list_level}--;
 
1023
    return $list_str;
 
1024
}
 
1025
 
 
1026
sub _DoCodeBlocks {
 
1027
#
 
1028
#   Process Markdown `<pre><code>` blocks.
 
1029
#   
 
1030
 
 
1031
    my ($self, $text) = @_;
 
1032
 
 
1033
        $text =~ s{
 
1034
                (?:\n\n|\A)
 
1035
                (                   # $1 = the code block -- one or more lines, starting with a space/tab
 
1036
                  (?:
 
1037
                    (?:[ ]{$self->{tab_width}} | \t)  # Lines must start with a tab or a tab-width of spaces
 
1038
                    .*\n+
 
1039
                  )+
 
1040
                )
 
1041
                ((?=^[ ]{0,$self->{tab_width}}\S)|\Z)   # Lookahead for non-space at line-start, or end of doc
 
1042
        }{
 
1043
        my $codeblock = $1;
 
1044
        my $result; # return value
 
1045
 
 
1046
        $codeblock = $self->_EncodeCode($self->_Outdent($codeblock));
 
1047
        $codeblock = $self->_Detab($codeblock);
 
1048
        $codeblock =~ s/\A\n+//; # trim leading newlines
 
1049
        $codeblock =~ s/\n+\z//; # trim trailing newlines
 
1050
 
 
1051
        $result = "\n\n<pre><code>" . $codeblock . "\n</code></pre>\n\n";
 
1052
 
 
1053
        $result;
 
1054
        }egmx;
 
1055
 
 
1056
        return $text;
 
1057
}
 
1058
 
 
1059
sub _DoCodeSpans {
 
1060
#
 
1061
#   *   Backtick quotes are used for <code></code> spans.
 
1062
 
1063
#   *   You can use multiple backticks as the delimiters if you want to
 
1064
#       include literal backticks in the code span. So, this input:
 
1065
#     
 
1066
#         Just type ``foo `bar` baz`` at the prompt.
 
1067
#     
 
1068
#       Will translate to:
 
1069
#     
 
1070
#         <p>Just type <code>foo `bar` baz</code> at the prompt.</p>
 
1071
#     
 
1072
#       There's no arbitrary limit to the number of backticks you
 
1073
#       can use as delimters. If you need three consecutive backticks
 
1074
#       in your code, use four for delimiters, etc.
 
1075
#
 
1076
#   *   You can use spaces to get literal backticks at the edges:
 
1077
#     
 
1078
#         ... type `` `bar` `` ...
 
1079
#     
 
1080
#       Turns to:
 
1081
#     
 
1082
#         ... type <code>`bar`</code> ...
 
1083
#
 
1084
 
 
1085
    my ($self, $text) = @_;
 
1086
 
 
1087
        $text =~ s@
 
1088
                        (?<!\\)         # Character before opening ` can't be a backslash
 
1089
                        (`+)            # $1 = Opening run of `
 
1090
                        (.+?)           # $2 = The code block
 
1091
                        (?<!`)
 
1092
                        \1                      # Matching closer
 
1093
                        (?!`)
 
1094
                @
 
1095
                        my $c = "$2";
 
1096
                        $c =~ s/^[ \t]*//g; # leading whitespace
 
1097
                        $c =~ s/[ \t]*$//g; # trailing whitespace
 
1098
                        $c = $self->_EncodeCode($c);
 
1099
                        "<code>$c</code>";
 
1100
                @egsx;
 
1101
 
 
1102
    return $text;
 
1103
}
 
1104
 
 
1105
sub _EncodeCode {
 
1106
#
 
1107
# Encode/escape certain characters inside Markdown code runs.
 
1108
# The point is that in code, these characters are literals,
 
1109
# and lose their special Markdown meanings.
 
1110
#
 
1111
    my $self = shift;
 
1112
    local $_ = shift;
 
1113
    
 
1114
    # Encode all ampersands; HTML entities are not
 
1115
    # entities within a Markdown code span.
 
1116
    s/&/&amp;/g;
 
1117
 
 
1118
    # Encode $'s, but only if we're running under Blosxom.
 
1119
    # (Blosxom interpolates Perl variables in article bodies.)
 
1120
    {
 
1121
        no warnings 'once';
 
1122
        if (defined($blosxom::version)) {
 
1123
            s/\$/&#036;/g;  
 
1124
        }
 
1125
    }
 
1126
 
 
1127
 
 
1128
    # Do the angle bracket song and dance:
 
1129
    s! <  !&lt;!gx;
 
1130
    s! >  !&gt;!gx;
 
1131
 
 
1132
    # Now, escape characters that are magic in Markdown:
 
1133
    s! \* !$g_escape_table{'*'}!ogx;
 
1134
    s! _  !$g_escape_table{'_'}!ogx;
 
1135
    s! {  !$g_escape_table{'{'}!ogx;
 
1136
    s! }  !$g_escape_table{'}'}!ogx;
 
1137
    s! \[ !$g_escape_table{'['}!ogx;
 
1138
    s! \] !$g_escape_table{']'}!ogx;
 
1139
    s! \\ !$g_escape_table{'\\'}!ogx;
 
1140
 
 
1141
    return $_;
 
1142
}
 
1143
 
 
1144
sub _DoItalicsAndBold {
 
1145
    my ($self, $text) = @_;
 
1146
 
 
1147
    # Handle at beginning of lines:
 
1148
    $text =~ s{ ^(\*\*|__) (?=\S) (.+?[*_]*) (?<=\S) \1 }
 
1149
        {<strong>$2</strong>}gsx;
 
1150
 
 
1151
    $text =~ s{ ^(\*|_) (?=\S) (.+?) (?<=\S) \1 }
 
1152
        {<em>$2</em>}gsx;
 
1153
 
 
1154
    # <strong> must go first:
 
1155
    $text =~ s{ (?<=\W) (\*\*|__) (?=\S) (.+?[*_]*) (?<=\S) \1 }
 
1156
        {<strong>$2</strong>}gsx;
 
1157
 
 
1158
    $text =~ s{ (?<=\W) (\*|_) (?=\S) (.+?) (?<=\S) \1 }
 
1159
        {<em>$2</em>}gsx;
 
1160
 
 
1161
    # And now, a second pass to catch nested strong and emphasis special cases
 
1162
    $text =~ s{ (?<=\W) (\*\*|__) (?=\S) (.+?[*_]*) (?<=\S) \1 }
 
1163
        {<strong>$2</strong>}gsx;
 
1164
 
 
1165
    $text =~ s{ (?<=\W) (\*|_) (?=\S) (.+?) (?<=\S) \1 }
 
1166
        {<em>$2</em>}gsx;
 
1167
 
 
1168
    return $text;
 
1169
}
 
1170
 
 
1171
sub _DoBlockQuotes {
 
1172
    my ($self, $text) = @_;
 
1173
 
 
1174
    $text =~ s{
 
1175
          (                             # Wrap whole match in $1
 
1176
            (
 
1177
              ^[ \t]*>[ \t]?            # '>' at the start of a line
 
1178
                .+\n                    # rest of the first line
 
1179
              (.+\n)*                   # subsequent consecutive lines
 
1180
              \n*                       # blanks
 
1181
            )+
 
1182
          )
 
1183
        }{
 
1184
            my $bq = $1;
 
1185
            $bq =~ s/^[ \t]*>[ \t]?//gm;    # trim one level of quoting
 
1186
            $bq =~ s/^[ \t]+$//mg;          # trim whitespace-only lines
 
1187
            $bq = $self->_RunBlockGamut($bq);      # recurse
 
1188
 
 
1189
            $bq =~ s/^/  /mg;
 
1190
            # These leading spaces screw with <pre> content, so we need to fix that:
 
1191
            $bq =~ s{
 
1192
                    (\s*<pre>.+?</pre>)
 
1193
                }{
 
1194
                    my $pre = $1;
 
1195
                    $pre =~ s/^  //mg;
 
1196
                    $pre;
 
1197
                }egsx;
 
1198
 
 
1199
            "<blockquote>\n$bq\n</blockquote>\n\n";
 
1200
        }egmx;
 
1201
 
 
1202
 
 
1203
    return $text;
 
1204
}
 
1205
 
 
1206
sub _FormParagraphs {
 
1207
#
 
1208
#   Params:
 
1209
#       $text - string to process with html <p> tags
 
1210
#
 
1211
    my ($self, $text) = @_;
 
1212
 
 
1213
    # Strip leading and trailing lines:
 
1214
    $text =~ s/\A\n+//;
 
1215
    $text =~ s/\n+\z//;
 
1216
 
 
1217
    my @grafs = split(/\n{2,}/, $text);
 
1218
 
 
1219
    #
 
1220
    # Wrap <p> tags.
 
1221
    #
 
1222
    foreach (@grafs) {
 
1223
        unless (defined( $self->{_html_blocks}{$_} )) {
 
1224
            $_ = $self->_RunSpanGamut($_);
 
1225
            s/^([ \t]*)/<p>/;
 
1226
            $_ .= "</p>";
 
1227
        }
 
1228
    }
 
1229
 
 
1230
    #
 
1231
    # Unhashify HTML blocks
 
1232
    #
 
1233
    foreach (@grafs) {
 
1234
        if (defined( $self->{_html_blocks}{$_} )) {
 
1235
            $_ = $self->{_html_blocks}{$_};
 
1236
        }
 
1237
    }
 
1238
 
 
1239
    return join "\n\n", @grafs;
 
1240
}
 
1241
 
 
1242
sub _EncodeAmpsAndAngles {
 
1243
# Smart processing for ampersands and angle brackets that need to be encoded.
 
1244
 
 
1245
    my ($self, $text) = @_;
 
1246
    return '' if (!defined $text or !length $text);
 
1247
 
 
1248
    # Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:
 
1249
    #   http://bumppo.net/projects/amputator/
 
1250
    $text =~ s/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/&amp;/g;
 
1251
 
 
1252
    # Encode naked <'s
 
1253
    $text =~ s{<(?![a-z/?\$!])}{&lt;}gi;
 
1254
    
 
1255
    # And >'s - added by Fletcher Penney
 
1256
#   $text =~ s{>(?![a-z/?\$!])}{&gt;}gi;
 
1257
#   Causes problems...
 
1258
 
 
1259
    # Remove encoding inside comments
 
1260
    $text =~ s{
 
1261
        (?<=<!--) # Begin comment
 
1262
        (.*?)     # Anything inside
 
1263
        (?=-->)   # End comments
 
1264
    }{
 
1265
        my $t = $1;
 
1266
        $t =~ s/&amp;/&/g;
 
1267
        $t =~ s/&lt;/</g;
 
1268
        $t;
 
1269
    }egsx;
 
1270
 
 
1271
    return $text;
 
1272
}
 
1273
 
 
1274
sub _EncodeBackslashEscapes {
 
1275
#
 
1276
#   Parameter:  String.
 
1277
#   Returns:    The string, with after processing the following backslash
 
1278
#               escape sequences.
 
1279
#
 
1280
    my $self = shift;
 
1281
    local $_ = shift;
 
1282
 
 
1283
    s! \\\\  !$g_escape_table{'\\'}!ogx;     # Must process escaped backslashes first.
 
1284
    s! \\`   !$g_escape_table{'`'}!ogx;
 
1285
    s! \\\*  !$g_escape_table{'*'}!ogx;
 
1286
    s! \\_   !$g_escape_table{'_'}!ogx;
 
1287
    s! \\\{  !$g_escape_table{'{'}!ogx;
 
1288
    s! \\\}  !$g_escape_table{'}'}!ogx;
 
1289
    s! \\\[  !$g_escape_table{'['}!ogx;
 
1290
    s! \\\]  !$g_escape_table{']'}!ogx;
 
1291
    s! \\\(  !$g_escape_table{'('}!ogx;
 
1292
    s! \\\)  !$g_escape_table{')'}!ogx;
 
1293
    s! \\>   !$g_escape_table{'>'}!ogx;
 
1294
    s! \\\#  !$g_escape_table{'#'}!ogx;
 
1295
    s! \\\+  !$g_escape_table{'+'}!ogx;
 
1296
    s! \\\-  !$g_escape_table{'-'}!ogx;
 
1297
    s! \\\.  !$g_escape_table{'.'}!ogx;
 
1298
    s{ \\!  }{$g_escape_table{'!'}}ogx;
 
1299
 
 
1300
    return $_;
 
1301
}
 
1302
 
 
1303
sub _DoAutoLinks {
 
1304
    my ($self, $text) = @_;
 
1305
 
 
1306
    $text =~ s{<((https?|ftp):[^'">\s]+)>}{<a href="$1">$1</a>}gi;
 
1307
 
 
1308
    # Email addresses: <address@domain.foo>
 
1309
    $text =~ s{
 
1310
        <
 
1311
        (?:mailto:)?
 
1312
        (
 
1313
            [-.\w]+
 
1314
            \@
 
1315
            [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+
 
1316
        )
 
1317
        >
 
1318
    }{
 
1319
        $self->_EncodeEmailAddress( $self->_UnescapeSpecialChars($1) );
 
1320
    }egix;
 
1321
 
 
1322
    return $text;
 
1323
}
 
1324
 
 
1325
sub _EncodeEmailAddress {
 
1326
#
 
1327
#   Input: an email address, e.g. "foo@example.com"
 
1328
#
 
1329
#   Output: the email address as a mailto link, with each character
 
1330
#       of the address encoded as either a decimal or hex entity, in
 
1331
#       the hopes of foiling most address harvesting spam bots. E.g.:
 
1332
#
 
1333
#     <a href="&#x6D;&#97;&#105;&#108;&#x74;&#111;:&#102;&#111;&#111;&#64;&#101;
 
1334
#       x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;">&#102;&#111;&#111;
 
1335
#       &#64;&#101;x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;</a>
 
1336
#
 
1337
#   Based on a filter by Matthew Wickline, posted to the BBEdit-Talk
 
1338
#   mailing list: <http://tinyurl.com/yu7ue>
 
1339
#
 
1340
 
 
1341
    my ($self, $addr) = @_;
 
1342
 
 
1343
    my @encode = (
 
1344
        sub { '&#' .                 ord(shift)   . ';' },
 
1345
        sub { '&#x' . sprintf( "%X", ord(shift) ) . ';' },
 
1346
        sub {                            shift          },
 
1347
    );
 
1348
 
 
1349
    $addr = "mailto:" . $addr;
 
1350
 
 
1351
    $addr =~ s{(.)}{
 
1352
        my $char = $1;
 
1353
        if ( $char eq '@' ) {
 
1354
            # this *must* be encoded. I insist.
 
1355
            $char = $encode[int rand 1]->($char);
 
1356
        } 
 
1357
        elsif ( $char ne ':' ) {
 
1358
            # leave ':' alone (to spot mailto: later)
 
1359
            my $r = rand;
 
1360
            # roughly 10% raw, 45% hex, 45% dec
 
1361
            $char = (
 
1362
                $r > .9   ?  $encode[2]->($char)  :
 
1363
                $r < .45  ?  $encode[1]->($char)  :
 
1364
                             $encode[0]->($char)
 
1365
            );
 
1366
        }
 
1367
        $char;
 
1368
    }gex;
 
1369
 
 
1370
    $addr = qq{<a href="$addr">$addr</a>};
 
1371
    $addr =~ s{">.+?:}{">}; # strip the mailto: from the visible part
 
1372
 
 
1373
    return $addr;
 
1374
}
 
1375
 
 
1376
sub _UnescapeSpecialChars {
 
1377
#
 
1378
# Swap back in all the special characters we've hidden.
 
1379
#
 
1380
    my ($self, $text) = @_;
 
1381
 
 
1382
    while( my($char, $hash) = each(%g_escape_table) ) {
 
1383
        $text =~ s/$hash/$char/g;
 
1384
    }
 
1385
    return $text;
 
1386
}
 
1387
 
 
1388
sub _TokenizeHTML {
 
1389
#
 
1390
#   Parameter:  String containing HTML markup.
 
1391
#   Returns:    Reference to an array of the tokens comprising the input
 
1392
#               string. Each token is either a tag (possibly with nested,
 
1393
#               tags contained therein, such as <a href="<MTFoo>">, or a
 
1394
#               run of text between tags. Each element of the array is a
 
1395
#               two-element array; the first is either 'tag' or 'text';
 
1396
#               the second is the actual value.
 
1397
#
 
1398
#
 
1399
#   Derived from the _tokenize() subroutine from Brad Choate's MTRegex plugin.
 
1400
#       <http://www.bradchoate.com/past/mtregex.php>
 
1401
#
 
1402
 
 
1403
    my ($self, $str) = @_;
 
1404
    my $pos = 0;
 
1405
    my $len = length $str;
 
1406
    my @tokens;
 
1407
 
 
1408
    my $depth = 6;
 
1409
    my $nested_tags = join('|', ('(?:<[a-z/!$](?:[^<>]') x $depth) . (')*>)' x  $depth);
 
1410
    my $match = qr/(?s: <! ( -- .*? -- \s* )+ > ) |  # comment
 
1411
                   (?s: <\? .*? \?> ) |              # processing instruction
 
1412
                   $nested_tags/iox;                   # nested tags
 
1413
 
 
1414
    while ($str =~ m/($match)/og) {
 
1415
        my $whole_tag = $1;
 
1416
        my $sec_start = pos $str;
 
1417
        my $tag_start = $sec_start - length $whole_tag;
 
1418
        if ($pos < $tag_start) {
 
1419
            push @tokens, ['text', substr($str, $pos, $tag_start - $pos)];
 
1420
        }
 
1421
        push @tokens, ['tag', $whole_tag];
 
1422
        $pos = pos $str;
 
1423
    }
 
1424
    push @tokens, ['text', substr($str, $pos, $len - $pos)] if $pos < $len;
 
1425
    \@tokens;
 
1426
}
 
1427
 
 
1428
sub _Outdent {
 
1429
#
 
1430
# Remove one level of line-leading tabs or spaces
 
1431
#
 
1432
    my ($self, $text) = @_;
 
1433
 
 
1434
    $text =~ s/^(\t|[ ]{1,$self->{tab_width}})//gm;
 
1435
    return $text;
 
1436
}
 
1437
 
 
1438
sub _Detab {
 
1439
#
 
1440
# Cribbed from a post by Bart Lateur:
 
1441
# <http://www.nntp.perl.org/group/perl.macperl.anyperl/154>
 
1442
#
 
1443
    my ($self, $text) = @_;
 
1444
 
 
1445
    # FIXME - Better anchor/regex would be quicker.
 
1446
    
 
1447
    # Original:
 
1448
    #$text =~ s{(.*?)\t}{$1.(' ' x ($self->{tab_width} - length($1) % $self->{tab_width}))}ge;
 
1449
    
 
1450
    # Much swifter, but pretty hateful:
 
1451
    do {} while ($text =~ s{^(.*?)\t}{$1.(' ' x ($self->{tab_width} - length($1) % $self->{tab_width}))}mge);
 
1452
    return $text;
 
1453
}
 
1454
 
 
1455
sub _ConvertCopyright {
 
1456
    my ($self, $text) = @_;
 
1457
    # Convert to an XML compatible form of copyright symbol
 
1458
    
 
1459
    $text =~ s/&copy;/&#xA9;/gi;
 
1460
    
 
1461
    return $text;
 
1462
}
 
1463
 
 
1464
1;
 
1465
 
 
1466
__END__
 
1467
 
 
1468
=head1 OTHER IMPLEMENTATIONS
 
1469
 
 
1470
Markdown has been re-implemented in a number of languages, and with a number of additions.
 
1471
 
 
1472
Those that I have found are listed below:
 
1473
 
 
1474
=over
 
1475
 
 
1476
=item C - <http://www.pell.portland.or.us/~orc/Code/discount>
 
1477
 
 
1478
Discount - Original Markdown, but in C. Fastest implementation available, and passes MDTest. 
 
1479
Adds it's own set of custom features.
 
1480
 
 
1481
=item python - <http://www.freewisdom.org/projects/python-markdown/>
 
1482
 
 
1483
Python Markdown which is mostly compatible with the original, with an interesting extension API.
 
1484
 
 
1485
=item ruby (maruku) - <http://maruku.rubyforge.org/>
 
1486
 
 
1487
One of the nicest implementations out there. Builds a parse tree internally so very flexible.
 
1488
 
 
1489
=item php - <http://michelf.com/projects/php-markdown/>
 
1490
 
 
1491
A direct port of Markdown.pl, also has a separately maintained 'extra' version, 
 
1492
which adds a number of features that were borrowed by MultiMarkdown.
 
1493
 
 
1494
=item lua - <http://www.frykholm.se/files/markdown.lua>
 
1495
 
 
1496
Port to lua. Simple and lightweight (as lua is).
 
1497
 
 
1498
=item haskell - <http://johnmacfarlane.net/pandoc/>
 
1499
 
 
1500
Pandoc is a more general library, supporting Markdown, reStructuredText, LaTeX and more.
 
1501
 
 
1502
=item javascript - <http://www.attacklab.net/showdown-gui.html>
 
1503
 
 
1504
Direct(ish) port of Markdown.pl to JavaScript
 
1505
 
 
1506
=back
 
1507
 
 
1508
=head1 BUGS
 
1509
 
 
1510
To file bug reports or feature requests please send email to:
 
1511
 
 
1512
    bug-Text-Markdown@rt.cpan.org
 
1513
    
 
1514
Please include with your report: (1) the example input; (2) the output
 
1515
you expected; (3) the output Markdown actually produced.
 
1516
 
 
1517
=head1 VERSION HISTORY
 
1518
 
 
1519
See the Changes file for detailed release notes for this version.
 
1520
 
 
1521
=head1 AUTHOR
 
1522
 
 
1523
    John Gruber
 
1524
    http://daringfireball.net/
 
1525
 
 
1526
    PHP port and other contributions by Michel Fortin
 
1527
    http://michelf.com/
 
1528
 
 
1529
    MultiMarkdown changes by Fletcher Penney
 
1530
    http://fletcher.freeshell.org/
 
1531
 
 
1532
    CPAN Module Text::MultiMarkdown (based on Text::Markdown by Sebastian
 
1533
    Riedel) originally by Darren Kulp (http://kulp.ch/)
 
1534
    
 
1535
    This module is maintained by: Tomas Doran http://www.bobtfish.net/
 
1536
 
 
1537
=head1 THIS DISTRIBUTION
 
1538
 
 
1539
Please note that this distribution is a fork of John Gruber's original Markdown project, 
 
1540
and it *is not* in any way blessed by him.
 
1541
 
 
1542
Whilst this code aims to be compatible with the original Markdown.pl (and incorporates 
 
1543
and passes the Markdown test suite) whilst fixing a number of bugs in the original - 
 
1544
there may be differences between the behavior of this module and Markdown.pl. If you find
 
1545
any differences where you believe Text::Markdown behaves contrary to the Markdown spec, 
 
1546
please report them as bugs.
 
1547
 
 
1548
Text::Markdown *does not* extend the markdown dialect in any way from that which is documented at
 
1549
daringfireball. If you want additional features, you should look at L<Text::MultiMarkdown>.
 
1550
 
 
1551
=head1 COPYRIGHT AND LICENSE
 
1552
 
 
1553
Original Code Copyright (c) 2003-2004 John Gruber   
 
1554
<http://daringfireball.net/>   
 
1555
All rights reserved.
 
1556
 
 
1557
MultiMarkdown changes Copyright (c) 2005-2006 Fletcher T. Penney   
 
1558
<http://fletcher.freeshell.org/>   
 
1559
All rights reserved.
 
1560
 
 
1561
Text::MultiMarkdown changes Copyright (c) 2006-2008 Darren Kulp
 
1562
<http://kulp.ch> and Tomas Doran <http://www.bobtfish.net>
 
1563
 
 
1564
Redistribution and use in source and binary forms, with or without
 
1565
modification, are permitted provided that the following conditions are
 
1566
met:
 
1567
 
 
1568
* Redistributions of source code must retain the above copyright notice,
 
1569
  this list of conditions and the following disclaimer.
 
1570
 
 
1571
* Redistributions in binary form must reproduce the above copyright
 
1572
  notice, this list of conditions and the following disclaimer in the
 
1573
  documentation and/or other materials provided with the distribution.
 
1574
 
 
1575
* Neither the name "Markdown" nor the names of its contributors may
 
1576
  be used to endorse or promote products derived from this software
 
1577
  without specific prior written permission.
 
1578
 
 
1579
This software is provided by the copyright holders and contributors "as
 
1580
is" and any express or implied warranties, including, but not limited
 
1581
to, the implied warranties of merchantability and fitness for a
 
1582
particular purpose are disclaimed. In no event shall the copyright owner
 
1583
or contributors be liable for any direct, indirect, incidental, special,
 
1584
exemplary, or consequential damages (including, but not limited to,
 
1585
procurement of substitute goods or services; loss of use, data, or
 
1586
profits; or business interruption) however caused and on any theory of
 
1587
liability, whether in contract, strict liability, or tort (including
 
1588
negligence or otherwise) arising in any way out of the use of this
 
1589
software, even if advised of the possibility of such damage.
 
1590
 
 
1591
=cut