/[socialtext-import]/Pod-Simple-Wiki/lib/Pod/Simple/Wiki.pm
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /Pod-Simple-Wiki/lib/Pod/Simple/Wiki.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3 - (show annotations)
Tue Nov 28 14:15:41 2006 UTC (17 years, 5 months ago) by dpavlin
File size: 20967 byte(s)
import upstream Pod-Simple-Wiki-0.05.tar.gz
1 package Pod::Simple::Wiki;
2
3 ###############################################################################
4 #
5 # Pod::Simple::Wiki - A class for creating Pod to Wiki filters.
6 #
7 #
8 # Copyright 2003-2005, John McNamara, jmcnamara@cpan.org
9 #
10 # Documentation after __END__
11 #
12
13 use strict;
14 #use Pod::Simple::Debug (5);
15 use Pod::Simple;
16 use vars qw(@ISA $VERSION);
17
18 @ISA = qw(Pod::Simple);
19 $VERSION = '0.05';
20
21 my $_debug = 0;
22
23
24 ###############################################################################
25 ###############################################################################
26 #
27 # The tag mappings for various Wiki text formats
28 #
29
30 my %tags = (
31 'wiki' => {
32 '<b>' => "'''",
33 '</b>' => "'''",
34 '<i>' => "''",
35 '</i>' => "''",
36 '<tt>' => '"',
37 '</tt>' => '"',
38 '<pre>' => '',
39 '</pre>' => "\n\n",
40
41 '<h1>' => "\n----\n'''",
42 '</h1>' => "'''\n\n",
43 '<h2>' => "\n'''''",
44 '</h2>' => "'''''\n\n",
45 '<h3>' => "\n''",
46 '</h3>' => "''\n\n",
47 '<h4>' => "\n",
48 '</h4>' => "\n\n",
49 },
50
51 'kwiki' => {
52 '<b>' => '*',
53 '</b>' => '*',
54 '<i>' => '/',
55 '</i>' => '/',
56 '<tt>' => '[=',
57 '</tt>' => ']',
58 '<pre>' => '',
59 '</pre>' => "\n\n",
60
61 '<h1>' => "\n----\n= ",
62 '</h1>' => " =\n\n",
63 '<h2>' => "\n== ",
64 '</h2>' => " ==\n\n",
65 '<h3>' => "\n=== ",
66 '</h3>' => " ===\n\n",
67 '<h4>' => "==== ",
68 '</h4>' => "\n\n",
69 },
70
71 'usemod' => {
72 '<b>' => '<b>',
73 '</b>' => '</b>',
74 '<i>' => '<i>',
75 '</i>' => '</i>',
76 '<tt>' => '<tt>',
77 '</tt>' => '</tt>',
78 '<pre>' => "\n<pre>\n",
79 '</pre>' => "\n</pre>\n\n",
80
81 '<h1>' => "\n= ",
82 '</h1>' => " =\n\n",
83 '<h2>' => "\n== ",
84 '</h2>' => " ==\n\n",
85 '<h3>' => "\n=== ",
86 '</h3>' => " ===\n",
87 '<h4>' => "\n==== ",
88 '</h4>' => " ====\n\n",
89 },
90
91 'usemod_classic' => {
92 '<b>' => "'''",
93 '</b>' => "'''",
94 '<i>' => "''",
95 '</i>' => "''",
96 '<tt>' => '<tt>',
97 '</tt>' => '</tt>',
98 '<pre>' => "\n<pre>\n",
99 '</pre>' => "\n</pre>\n\n",
100
101 '<h1>' => "\n= ",
102 '</h1>' => " =\n\n",
103 '<h2>' => "\n== ",
104 '</h2>' => " ==\n\n",
105 '<h3>' => "\n=== ",
106 '</h3>' => " ===\n",
107 '<h4>' => "\n==== ",
108 '</h4>' => " ====\n\n",
109 },
110
111
112 'twiki' => {
113 '<b>' => "*",
114 '</b>' => "*",
115 '<i>' => "_",
116 '</i>' => "_",
117 '<tt>' => '=',
118 '</tt>' => '=',
119 '<pre>' => "\n<verbatim>\n",
120 '</pre>' => "\n</verbatim>\n\n",
121
122 '<h1>' => "---+ ",
123 '</h1>' => "\n\n",
124 '<h2>' => "---++ ",
125 '</h2>' => "\n\n",
126 '<h3>' => "---+++ ",
127 '</h3>' => "\n\n",
128 '<h4>' => "---++++ ",
129 '</h4>' => "\n\n",
130 },
131 'wikipedia' => {
132 '<b>' => "'''",
133 '</b>' => "'''",
134 '<i>' => "''",
135 '</i>' => "''",
136 '<tt>' => '<tt>',
137 '</tt>' => '</tt>',
138 '<pre>' => "\n<code>\n",
139 '</pre>' => "\n</code>\n",
140
141 '<h1>' => "==",
142 '</h1>' => "==\n",
143 '<h2>' => "===",
144 '</h2>' => "===\n",
145 '<h3>' => "====",
146 '</h3>' => "====\n",
147 '<h4>' => "=====",
148 '</h4>' => "=====\n",
149 },
150 );
151
152
153 ###############################################################################
154 #
155 # new()
156 #
157 # Simple constructor inheriting from Pod::Simple.
158 #
159 sub new {
160
161 my $class = shift;
162 my $format = lc shift || 'wiki';
163 $format = 'wikipedia' if $format eq 'mediawiki';
164 $format = 'wiki' unless exists $tags{$format};
165
166 my $self = Pod::Simple->new(@_);
167 $self->{_wiki_text} = '';
168 $self->{_format} = $format;
169 $self->{_tags} = $tags{$format};
170 $self->{output_fh} ||= *STDOUT{IO};
171
172 bless $self, $class;
173 return $self;
174 }
175
176
177 ###############################################################################
178 #
179 # _append()
180 #
181 # Appends some text to the buffered Wiki text.
182 #
183 sub _append {
184
185 my $self = shift;
186
187 $self->{_wiki_text} .= $_[0];
188 }
189
190
191 ###############################################################################
192 #
193 # _output()
194 #
195 # Appends some text to the buffered Wiki text and then emits it. Also resets
196 # the buffer.
197 #
198 sub _output {
199
200 my $self = shift;
201 my $text = $_[0];
202
203 $text = '' unless defined $text;
204
205 print {$self->{output_fh}} $self->{_wiki_text}, $text;
206
207 $self->{_wiki_text} = '';
208 }
209
210
211 ###############################################################################
212 #
213 # _indent_item()
214 #
215 # Indents an "over-item" to the correct level.
216 #
217 sub _indent_item {
218
219 my $self = shift;
220 my $item_type = $_[0];
221 my $item_param = $_[1];
222 my $indent_level = $self->{_item_indent};
223
224 if ($self->{_format} eq 'wiki') {
225
226 if ($item_type eq 'bullet') {
227 $self->_append("*" x $indent_level);
228 # This was the way C2 Wiki used to define a bullet list
229 # $self->_append("\t" x $indent_level . '*');
230 }
231 elsif ($item_type eq 'number') {
232 $self->_append("\t" x $indent_level . $item_param);
233 }
234 elsif ($item_type eq 'text') {
235 $self->_append("\t" x $indent_level);
236 }
237 }
238 elsif ($self->{_format} eq 'kwiki') {
239
240 if ($item_type eq 'bullet') {
241 $self->_append('*' x $indent_level . ' ');
242 }
243 elsif ($item_type eq 'number') {
244 $self->_append('0' x $indent_level . ' ');
245 }
246 elsif ($item_type eq 'text') {
247 $self->_append(";" x $indent_level . ' ');
248 }
249 }
250 elsif ($self->{_format} eq 'usemod') {
251
252 if ($item_type eq 'bullet') {
253 $self->_append('*' x $indent_level);
254 }
255 elsif ($item_type eq 'number') {
256 $self->_append('#' x $indent_level);
257 }
258 elsif ($item_type eq 'text') {
259 $self->_append(";" x $indent_level);
260 }
261 }
262 elsif ($self->{_format} eq 'twiki') {
263
264 if ($item_type eq 'bullet') {
265 $self->_append(' ' x $indent_level . "* ");
266 }
267 elsif ($item_type eq 'number') {
268 $self->_append(' ' x $indent_level . $item_param . ". ");
269 }
270 elsif ($item_type eq 'text') {
271 $self->_append(' ' x $indent_level . '$ ' );
272 }
273 }
274 elsif ($self->{_format} eq 'wikipedia') {
275
276 if ($item_type eq 'bullet') {
277 $self->_append('*' x $indent_level . ' ');
278 }
279 elsif ($item_type eq 'number') {
280 $self->_append('#' x $indent_level . ' ');
281 }
282 elsif ($item_type eq 'text') {
283 $self->_append(";" x $indent_level . ' ');
284 }
285 }
286 }
287
288
289 ###############################################################################
290 #
291 # _skip_headings()
292 #
293 # Formatting in headings doesn't look great or is ignored in some formats.
294 #
295 sub _skip_headings {
296
297 my $self = shift;
298
299 return (
300 $self->{_format} eq 'kwiki' and
301 ($self->{_in_head1} or
302 $self->{_in_head2} or
303 $self->{_in_head3} or
304 $self->{_in_head4})
305 );
306 }
307
308
309 ###############################################################################
310 #
311 # _append_tag()
312 #
313 # Add an open or close tag to the current text.
314 #
315 sub _append_tag {
316
317 my $self = shift;
318 my $tag = $_[0];
319
320 $self->_append($self->{_tags}->{$tag});
321 }
322
323
324 ###############################################################################
325 ###############################################################################
326 #
327 # The methods in the following section are required by Pod::Simple to handle
328 # Pod directives and elements.
329 #
330 # The methods _handle_element_start() _handle_element_end() and _handle_text()
331 # are called by Pod::Simple in response to Pod constructs. We use
332 # _handle_element_start() and _handle_element_end() to generate calls to more
333 # specific methods. This is basically a long-hand version of Pod::Simple::
334 # Methody with the addition of location tracking.
335 #
336
337
338 ###############################################################################
339 #
340 # _handle_element_start()
341 #
342 # Call a method to handle the start of a element if one has been defined.
343 # We also set a flag to indicate that we are "in" the element type.
344 #
345 sub _handle_element_start {
346
347 my $self = shift;
348 my $element = $_[0];
349
350 $element =~ tr/-/_/;
351
352 print "<$element>\n" if $_debug;
353
354 $self->{"_in_". $element}++;
355
356 if (my $method = $self->can('_start_' . $element)) {
357 $method->($self, $_[1]);
358 }
359 }
360
361
362 ###############################################################################
363 #
364 # _handle_element_end()
365 #
366 # Call a method to handle the end of a element if one has been defined.
367 # We also set a flag to indicate that we are "out" of the element type.
368 #
369 sub _handle_element_end {
370
371 my $self = shift;
372 my $element = $_[0];
373
374 $element =~ tr/-/_/;
375
376 if (my $method = $self->can('_end_' . $element)) {
377 $method->($self);
378 }
379
380 $self->{"_in_". $element}--;
381
382 print "</$element>\n" if $_debug;
383 }
384
385
386 ###############################################################################
387 #
388 # _handle_text()
389 #
390 # Perform any necessary transforms on the text. This is mainly used to escape
391 # inadvertent CamelCase words.
392 #
393 sub _handle_text {
394
395 my $self = shift;
396 my $text = $_[0];
397
398 # Only escape CamelCase in Kwiki paragraphs
399 if ($self->{_format} eq 'kwiki' and not $self->{_in_Para}) {
400 $self->{_wiki_text} .= $text;
401 return;
402 }
403
404 # Split the text into tokens but maintain the whitespace
405 my @tokens = split /(\s+)/, $text;
406
407 if ($self->{_format} eq 'wiki') {
408 for (@tokens) {
409 next unless /\S/; # Ignore the whitespace
410 next if m[^(ht|f)tp://]; # Ignore URLs
411 s/([A-Z][a-z]+)(?=[A-Z])/$1''''''/g # Escape with 6 single quotes
412
413 }
414 }
415 elsif ($self->{_format} eq 'kwiki') {
416 for (@tokens) {
417 next unless /\S/; # Ignore the whitespace
418 next if m[^(ht|f)tp://]; # Ignore URLs
419 s/([A-Z][a-z]+[A-Z]\w+)/!$1/g; # Escape with !
420 }
421 }
422 # TODO: Add usemod <nowiki> escapes
423
424 # Rejoin the tokens and whitespace.
425 $self->{_wiki_text} .= join '', @tokens;
426 }
427
428
429 ###############################################################################
430 #
431 # Functions to deal with the I<>, B<> and C<> formatting codes.
432 #
433 sub _start_I {$_[0]->_append_tag('<i>') unless $_[0]->_skip_headings()}
434 sub _start_B {$_[0]->_append_tag('<b>') unless $_[0]->_skip_headings()}
435 sub _start_C {$_[0]->_append_tag('<tt>') unless $_[0]->_skip_headings()}
436 sub _start_F {$_[0]->_start_I}
437
438 sub _end_I {$_[0]->_append_tag('</i>') unless $_[0]->_skip_headings()}
439 sub _end_B {$_[0]->_append_tag('</b>') unless $_[0]->_skip_headings()}
440 sub _end_C {$_[0]->_append_tag('</tt>') unless $_[0]->_skip_headings()}
441 sub _end_F {$_[0]->_end_I}
442
443
444 ###############################################################################
445 #
446 # Functions to deal with the Pod =head directives
447 #
448 sub _start_head1 {$_[0]->_append_tag('<h1>')}
449 sub _start_head2 {$_[0]->_append_tag('<h2>')}
450 sub _start_head3 {$_[0]->_append_tag('<h3>')}
451 sub _start_head4 {$_[0]->_append_tag('<h4>')}
452
453 sub _end_head1 {$_[0]->_append_tag('</h1>'); $_[0]->_output()}
454 sub _end_head2 {$_[0]->_append_tag('</h2>'); $_[0]->_output()}
455 sub _end_head3 {$_[0]->_append_tag('</h3>'); $_[0]->_output()}
456 sub _end_head4 {$_[0]->_append_tag('</h4>'); $_[0]->_output()}
457
458
459 ###############################################################################
460 #
461 # Functions to deal with verbatim paragraphs. We emit the text "as is" for now.
462 # TODO: escape any Wiki formatting in text such as ''code''.
463 #
464 sub _start_Verbatim {$_[0]->_append_tag('<pre>')}
465 sub _end_Verbatim {$_[0]->_append_tag('</pre>'); $_[0]->_output()}
466
467
468 ###############################################################################
469 #
470 # Functions to deal with =over ... =back regions for
471 #
472 # Bulleted lists
473 # Numbered lists
474 # Text lists
475 # Block lists
476 #
477 sub _start_over_bullet {$_[0]->{_item_indent}++}
478 sub _start_over_number {$_[0]->{_item_indent}++}
479 sub _start_over_text {$_[0]->{_item_indent}++}
480
481 sub _end_over_bullet {$_[0]->{_item_indent}--;
482 $_[0]->_output("\n") unless $_[0]->{_item_indent}}
483
484 sub _end_over_number {$_[0]->{_item_indent}--;
485 $_[0]->_output("\n") unless $_[0]->{_item_indent}}
486
487 sub _end_over_text {$_[0]->{_item_indent}--;
488 $_[0]->_output("\n") unless $_[0]->{_item_indent}}
489
490 sub _start_item_bullet {$_[0]->_indent_item('bullet')}
491 sub _start_item_number {$_[0]->_indent_item('number', $_[1]->{number})}
492 sub _start_item_text {$_[0]->_indent_item('text')}
493
494 sub _end_item_bullet {$_[0]->_output("\n")}
495 sub _end_item_number {$_[0]->_output("\n")}
496 sub _end_item_text {$_[0]->_output(":\t") if $_[0]->{_format} eq 'wiki';
497 $_[0]->_output(" ; ") if $_[0]->{_format} eq 'kwiki';
498 $_[0]->_output(":" ) if $_[0]->{_format} eq 'usemod';
499 $_[0]->_output(": " ) if $_[0]->{_format} eq 'twiki';
500 $_[0]->_output(" : ") if $_[0]->{_format} eq 'wikipedia';}
501
502 sub _start_over_block {$_[0]->{_item_indent}++}
503 sub _end_over_block {$_[0]->{_item_indent}--}
504
505
506 ###############################################################################
507 #
508 # _start_Para()
509 #
510 # Special handling for paragraphs that are part of an "over" block.
511 #
512 sub _start_Para {
513
514 my $self = shift;
515 my $indent_level = $self->{_item_indent};
516
517 if ($self->{_in_over_block}) {
518
519 if ($self->{_format} eq 'wiki') {
520 $self->_append(("\t" x $indent_level) . " :\t");
521 }
522 elsif ($self->{_format} eq 'usemod') {
523 $self->_append(":" x $indent_level);
524 }
525 }
526 }
527
528
529 ###############################################################################
530 #
531 # _end_Para()
532 #
533 # Special handling for paragraphs that are part of an "over_text" block.
534 # This is mainly required be Kwiki.
535 #
536 sub _end_Para {
537
538 my $self = shift;
539
540 # Only add a newline if the paragraph isn't part of a text
541 if ($self->{_in_over_text}) {
542 # Workaround for the fact that Kwiki doesn't have a definition block
543 #$self->_output("\n") if $self->{_format} eq 'kwiki';
544 }
545 else {
546 $self->_output("\n");
547 }
548
549 $self->_output("\n")
550 }
551
552
553 1;
554
555
556 __END__
557
558 =head1 NAME
559
560 Pod::Simple::Wiki - A class for creating Pod to Wiki filters.
561
562 =head1 SYNOPSIS
563
564 To create a simple C<pod2wiki> filter:
565
566 #!/usr/bin/perl -w
567
568 use strict;
569 use Pod::Simple::Wiki;
570
571
572 my $parser = Pod::Simple::Wiki->new();
573
574 if (defined $ARGV[0]) {
575 open IN, $ARGV[0] or die "Couldn't open $ARGV[0]: $!\n";
576 } else {
577 *IN = *STDIN;
578 }
579
580 if (defined $ARGV[1]) {
581 open OUT, ">$ARGV[1]" or die "Couldn't open $ARGV[1]: $!\n";
582 } else {
583 *OUT = *STDOUT;
584 }
585
586 $parser->output_fh(*OUT);
587 $parser->parse_file(*IN);
588
589 __END__
590
591
592 =head1 DESCRIPTION
593
594 The C<Pod::Simple::Wiki> module is used for converting Pod text to Wiki text.
595
596 Pod (Plain Old Documentation) is a simple markup language used for writing Perl documentation.
597
598 A Wiki is a user extensible web site. It uses very simple mark-up that is converted to Html.
599
600 For an introduction to Wikis see: http://en.wikipedia.org/wiki/Wiki
601
602
603 =head1 METHODS
604
605 =head2 new()
606
607 The C<new> method is used to create a new L<Pod::Simple::Wiki> object. It is also used to set the output Wiki format.
608
609 my $parser1 = Pod::Simple::Wiki->new('wiki');
610 my $parser2 = Pod::Simple::Wiki->new('kwiki');
611 my $parser3 = Pod::Simple::Wiki->new(); # Defaults to 'wiki'
612
613 The currently supported formats are:
614
615 =over 4
616
617 =item wiki
618
619 This is the original Wiki format as used on Ward Cunningham's Portland repository of Patterns. The formatting rules are given at http://c2.com/cgi/wiki?TextFormattingRules
620
621 =item kwiki
622
623 This is the format as used by Brian Ingerson's CGI::Kwiki: http://search.cpan.org/dist/CGI-Kwiki/
624
625 =item usemod
626
627 This is the format used by the Usemod wikis. See: http://www.usemod.com/cgi-bin/wiki.pl?WikiFormat
628
629 =item twiki
630
631 This is the format used by TWiki wikis. See: http://www.twiki.org/
632
633 =item wikipedia or mediawiki
634
635 This is the format used by Wikipedia and MediaWiki wikis. See: http://www.wikipedia.org/
636
637 =back
638
639 If no format is specified the parser defaults to C<wiki>.
640
641 Any other parameters in C<new> will be passed on to the parent L<Pod::Simple> object. See L<Pod::Simple> for more details.
642
643
644 =head2 Other methods
645
646 Pod::Simple::Wiki inherits all of the methods of L<Pod::Simple>. See L<Pod::Simple> for more details.
647
648
649 =head1 TODO
650
651 =over 4
652
653 =item *
654
655 Add more code, more tests and a few more users if possible.
656
657 =item *
658
659 Add other Wiki formats. Send requests or patches.
660
661 =item *
662
663 Fix some of the C<=over> edge cases. See the TODOs in the test programs.
664
665 =back
666
667
668
669 =head1 SEE ALSO
670
671 This module also installs a C<pod2wiki> command line utility. See C<pod2wiki --help> for details.
672
673
674 =head1 ACKNOWLEDGEMENTS
675
676 Thanks to Sean M. Burke for C<Pod::Simple>. It may not be simple but sub-classing it is. C<:-)>
677
678 Thanks to Sam Tregar for TWiki support.
679
680 Thanks Tony Sidaway for Wikipedia/MediaWiki support.
681
682
683 =head1 AUTHOR
684
685 John McNamara jmcnamara@cpan.org
686
687
688 =head1 COPYRIGHT
689
690 © MMIII-MMV, John McNamara.
691
692 All Rights Reserved. This module is free software. It may be used, redistributed and/or modified under the same terms as Perl itself.
693

  ViewVC Help
Powered by ViewVC 1.1.26