blib/lib/XHTML/MediaWiki.pm | |||
---|---|---|---|
Criterion | Covered | Total | % |
statement | 414 | 608 | 68.0 |
branch | 83 | 204 | 40.6 |
condition | 21 | 71 | 29.5 |
subroutine | 80 | 109 | 73.3 |
pod | 10 | 10 | 100.0 |
total | 608 | 1002 | 60.6 |
line | stmt | bran | cond | sub | pod | time | code |
---|---|---|---|---|---|---|---|
1 | 2 | 2 | 62794 | use warnings; | |||
2 | 5 | ||||||
2 | 172 | ||||||
2 | 2 | 2 | 13 | use strict; | |||
2 | 4 | ||||||
2 | 208 | ||||||
3 | |||||||
4 | package XHTML::MediaWiki; | ||||||
5 | #XHTML::MediaWiki:: | ||||||
6 | |||||||
7 | =head1 NAME | ||||||
8 | |||||||
9 | XHTML::MediaWiki - Translate Wiki markup into xhtml | ||||||
10 | |||||||
11 | =cut | ||||||
12 | |||||||
13 | our $VERSION = '0.11'; | ||||||
14 | $VERSION = eval $VERSION; | ||||||
15 | |||||||
16 | our $DEBUG = 0; | ||||||
17 | |||||||
18 | =head1 SYNOPSIS | ||||||
19 | |||||||
20 | use XHTML::MediaWiki; | ||||||
21 | my $mediawiki = XHTML::MediaWiki->new( link_path => "http://example.com/base/" ); | ||||||
22 | my $xhtm = $mediawiki->format($text); | ||||||
23 | |||||||
24 | =head1 DESCRIPTION | ||||||
25 | |||||||
26 | L |
||||||
27 | Mediawiki to format their pages. This module attempts to duplicate the | ||||||
28 | Mediawiki formatting rules. Those formatting rules can be simple and | ||||||
29 | easy to use, while providing more advanced options for the power user. | ||||||
30 | |||||||
31 | =cut | ||||||
32 | |||||||
33 | 2 | 2 | 11 | use Carp qw(carp confess croak); | |||
2 | 4 | ||||||
2 | 559 | ||||||
34 | 2 | 2 | 6370 | use CGI qw(:standard); | |||
2 | 70192 | ||||||
2 | 18 | ||||||
35 | 2 | 2 | 12341 | use Scalar::Util qw(blessed); | |||
2 | 7 | ||||||
2 | 438 | ||||||
36 | |||||||
37 | 2 | 2 | 3226 | use HTML::Parser; | |||
2 | 20343 | ||||||
2 | 473 | ||||||
38 | |||||||
39 | =head2 Constructors | ||||||
40 | |||||||
41 | =over 4 | ||||||
42 | |||||||
43 | =item * new( link_path => 'base path' ) | ||||||
44 | |||||||
45 | Create a new XHTML:;MediaWiki object. C |
||||||
46 | for hyperlinks. | ||||||
47 | |||||||
48 | =back | ||||||
49 | |||||||
50 | =cut | ||||||
51 | |||||||
52 | sub new | ||||||
53 | { | ||||||
54 | 2 | 2 | 1 | 808 | my $class = shift; | ||
55 | |||||||
56 | 2 | 15 | bless { | ||||
57 | link_path => '', | ||||||
58 | @_ | ||||||
59 | }, $class; | ||||||
60 | } | ||||||
61 | |||||||
62 | =head2 Methods | ||||||
63 | |||||||
64 | =over 4 | ||||||
65 | |||||||
66 | =item * format() | ||||||
67 | |||||||
68 | The format method is the only method that needs to be called for the | ||||||
69 | normal operation of this object. You call format() with the raw I |
||||||
70 | it returns the xhtml representation of that I |
||||||
71 | |||||||
72 | =cut | ||||||
73 | |||||||
74 | sub format | ||||||
75 | { | ||||||
76 | 1 | 1 | 1 | 7 | my $self = shift; | ||
77 | 1 | 2 | my $raw = shift; | ||||
78 | |||||||
79 | 1 | 4 | my $cooked = $self->_format($raw); | ||||
80 | |||||||
81 | 1 | 4 | return $cooked; | ||||
82 | } | ||||||
83 | |||||||
84 | =item * reset_counters() | ||||||
85 | |||||||
86 | Call this method to reset the footnote counter. | ||||||
87 | |||||||
88 | =back | ||||||
89 | |||||||
90 | =cut | ||||||
91 | |||||||
92 | sub reset_counters | ||||||
93 | { | ||||||
94 | 0 | 0 | 1 | 0 | my $self = shift; | ||
95 | |||||||
96 | 0 | 0 | $self->{footnote} = 0; | ||||
97 | } | ||||||
98 | |||||||
99 | =head2 Overridable Methods | ||||||
100 | |||||||
101 | The following methods can be overridden to change the functionality of | ||||||
102 | the object. | ||||||
103 | |||||||
104 | =over 4 | ||||||
105 | |||||||
106 | =item * get_block() | ||||||
107 | |||||||
108 | If you would like to override the Block objects you can override this method. | ||||||
109 | |||||||
110 | =cut | ||||||
111 | |||||||
112 | sub get_block | ||||||
113 | { | ||||||
114 | 5 | 5 | 1 | 7 | my $self = shift; | ||
115 | 5 | 6 | my $type = shift; | ||||
116 | |||||||
117 | 5 | 50 | 21 | my $ret = 'XHTML::MediaWiki::Block::' . ucfirst($type || 'special'); | |||
118 | } | ||||||
119 | |||||||
120 | # This sub recognizes three states: | ||||||
121 | # | ||||||
122 | # 1. undef | ||||||
123 | # Normal wiki processing will be done on this line. | ||||||
124 | # | ||||||
125 | # 2. html | ||||||
126 | # Links and phrasal processing will be done, but formatting should be | ||||||
127 | # ignored. | ||||||
128 | # | ||||||
129 | # 3. nowiki | ||||||
130 | # No further wiki processing should be done. | ||||||
131 | # | ||||||
132 | # Each state may override the lower ones if already set on a given line. | ||||||
133 | # | ||||||
134 | |||||||
135 | { | ||||||
136 | package XHTML::MediaWiki::Parser::Block::Line; | ||||||
137 | |||||||
138 | 2 | 2 | 2051 | use Params::Validate qw (validate); | |||
2 | 26345 | ||||||
2 | 440 | ||||||
139 | |||||||
140 | sub new | ||||||
141 | { | ||||||
142 | 9 | 9 | 11 | my $class = shift; | |||
143 | 9 | 150 | my %p = validate(@_, { | ||||
144 | state => 1, | ||||||
145 | text => { | ||||||
146 | default => '', | ||||||
147 | }, | ||||||
148 | eol => 0, | ||||||
149 | }); | ||||||
150 | |||||||
151 | 9 | 68 | my $self = bless { %p }, $class; | ||||
152 | |||||||
153 | 9 | 77 | return $self; | ||||
154 | } | ||||||
155 | |||||||
156 | sub state | ||||||
157 | { | ||||||
158 | 10 | 10 | 73 | shift->{state}; | |||
159 | } | ||||||
160 | |||||||
161 | sub append | ||||||
162 | { | ||||||
163 | 26 | 26 | 27 | my $self = shift; | |||
164 | 26 | 30 | my $text = shift; | ||||
165 | 26 | 100 | $self->{text} .= $text; | ||||
166 | } | ||||||
167 | |||||||
168 | } | ||||||
169 | { | ||||||
170 | package XHTML::MediaWiki::Parser::Block; | ||||||
171 | |||||||
172 | 2 | 2 | 18 | use Params::Validate qw (validate); | |||
2 | 5 | ||||||
2 | 82 | ||||||
173 | 2 | 2 | 9 | use Carp qw(croak); | |||
2 | 5 | ||||||
2 | 3841 | ||||||
174 | |||||||
175 | sub new | ||||||
176 | { | ||||||
177 | 6 | 6 | 7 | my $class = shift; | |||
178 | 6 | 92 | my %p = validate(@_, { | ||||
179 | type => 1, | ||||||
180 | level => 0, | ||||||
181 | }); | ||||||
182 | 6 | 50 | 33 | 40 | croak("internal error") if ($p{type} eq 'unordered' && !$p{level}); | ||
183 | 6 | 36 | my $self = | ||||
184 | bless { | ||||||
185 | lines => [], | ||||||
186 | %p, | ||||||
187 | }, $class; | ||||||
188 | |||||||
189 | 6 | 21 | return $self; | ||||
190 | } | ||||||
191 | |||||||
192 | sub block_type | ||||||
193 | { | ||||||
194 | 5 | 5 | 10 | shift->{type}; | |||
195 | } | ||||||
196 | |||||||
197 | sub args | ||||||
198 | { | ||||||
199 | 5 | 5 | 7 | my $self = shift; | |||
200 | 5 | 50 | 20 | push(@{$self->{lines}}, $self->{line}) if $self->{line}; | |||
5 | 13 | ||||||
201 | return ( | ||||||
202 | 5 | 35 | lines => $self->{lines}, | ||||
203 | (level => $self->{level}) x!! $self->{level}, | ||||||
204 | ); | ||||||
205 | } | ||||||
206 | |||||||
207 | sub get_line | ||||||
208 | { | ||||||
209 | 15 | 15 | 15 | my $self = shift; | |||
210 | |||||||
211 | 15 | 66 | 53 | $self->{line} ||= XHTML::MediaWiki::Parser::Block::Line->new( state => 'wiki'); | |||
212 | } | ||||||
213 | |||||||
214 | sub get_state | ||||||
215 | { | ||||||
216 | 10 | 10 | 9 | my $self = shift; | |||
217 | |||||||
218 | 10 | 20 | $self->{type}; | ||||
219 | } | ||||||
220 | |||||||
221 | sub in_nowiki | ||||||
222 | { | ||||||
223 | 10 | 10 | 11 | my $self = shift; | |||
224 | 10 | 11 | my $line = $self->{line}; | ||||
225 | |||||||
226 | 10 | 50 | 16 | if ($line) { | |||
227 | 10 | 18 | return $line->state eq 'nowiki'; | ||||
228 | } else { | ||||||
229 | 0 | 0 | return 0; | ||||
230 | } | ||||||
231 | } | ||||||
232 | |||||||
233 | sub append_text | ||||||
234 | { | ||||||
235 | 15 | 15 | 12 | my $self = shift; | |||
236 | 15 | 20 | my $text = shift; | ||||
237 | 15 | 50 | 27 | die "extra arguments" if @_; | |||
238 | |||||||
239 | 15 | 28 | my $line = $self->get_line(); | ||||
240 | 15 | 33 | $line->append($text); | ||||
241 | } | ||||||
242 | |||||||
243 | sub set_nowiki | ||||||
244 | { | ||||||
245 | 0 | 0 | 0 | my $self = shift; | |||
246 | |||||||
247 | 0 | 0 | 0 | push(@{$self->{lines}}, $self->{line}) if $self->{line}; | |||
0 | 0 | ||||||
248 | 0 | 0 | $self->{line} = XHTML::MediaWiki::Parser::Block::Line->new(state => 'nowiki'); | ||||
249 | } | ||||||
250 | |||||||
251 | sub set_wiki | ||||||
252 | { | ||||||
253 | 0 | 0 | 0 | my $self = shift; | |||
254 | |||||||
255 | 0 | 0 | 0 | push(@{$self->{lines}}, $self->{line}) if $self->{line}; | |||
0 | 0 | ||||||
256 | 0 | 0 | $self->{line} = XHTML::MediaWiki::Parser::Block::Line->new(state => 'wiki'); | ||||
257 | } | ||||||
258 | |||||||
259 | sub is_paragraph | ||||||
260 | { | ||||||
261 | 0 | 0 | 0 | my $self = shift; | |||
262 | |||||||
263 | 0 | 0 | return $self->{type} eq 'paragraph'; | ||||
264 | } | ||||||
265 | |||||||
266 | sub is_prewiki | ||||||
267 | { | ||||||
268 | 8 | 8 | 9 | my $self = shift; | |||
269 | |||||||
270 | 8 | 21 | return $self->{type} eq 'prewiki'; | ||||
271 | } | ||||||
272 | |||||||
273 | sub set_end_line | ||||||
274 | { | ||||||
275 | 11 | 11 | 12 | my $self = shift; | |||
276 | 11 | 50 | 26 | my $cnt = shift or croak "need count"; | |||
277 | |||||||
278 | 11 | 14 | my $line = $self->{line}; | ||||
279 | 11 | 100 | 21 | if (!defined $line) { | |||
280 | 3 | 33 | 83 | $line = $self->{lines}[-1] || XHTML::MediaWiki::Parser::Block::Line->new(state => 'dummy'); | |||
281 | 3 | 11 | $line->{eol} = $cnt; | ||||
282 | } | ||||||
283 | 11 | 25 | for (my $x = 0; $x < $cnt; $x++) { | ||||
284 | 11 | 19 | $line->append("\n");; | ||||
285 | } | ||||||
286 | 11 | 27 | $self; | ||||
287 | } | ||||||
288 | } | ||||||
289 | |||||||
290 | =item * encode() | ||||||
291 | |||||||
292 | You can override the encode function if you would like to change | ||||||
293 | what is encoded. Currently only &, <, and > are encoded. | ||||||
294 | |||||||
295 | =cut | ||||||
296 | |||||||
297 | sub encode | ||||||
298 | { | ||||||
299 | 13 | 13 | 1 | 17 | my $text = shift; | ||
300 | 13 | 50 | 24 | if (defined $text) { | |||
301 | 13 | 17 | $text =~ s{&}{&}gso; | ||||
302 | 13 | 14 | $text =~ s{<}{<}gso; | ||||
303 | 13 | 17 | $text =~ s{>}{>}gso; | ||||
304 | } | ||||||
305 | 13 | 23 | return $text; | ||||
306 | } | ||||||
307 | |||||||
308 | sub _close_to | ||||||
309 | { | ||||||
310 | 1 | 1 | 2 | my $parser = shift; | |||
311 | 1 | 2 | my $tag = shift; | ||||
312 | 1 | 2 | my $tagstack = $parser->{tag_stack}; | ||||
313 | 1 | 2 | my $text = ''; | ||||
314 | |||||||
315 | 1 | 50 | 3 | if (!@$tagstack) { | |||
316 | 0 | 0 | 0 | $text .= "" if $DEBUG; | |||
317 | # ignore extra closing tags | ||||||
318 | } else { | ||||||
319 | 1 | 4 | while (my $toptag = pop @$tagstack) { | ||||
320 | 1 | 2 | $text .= "$toptag>"; | ||||
321 | 1 | 50 | 4 | last if $tag eq $toptag; | |||
322 | } | ||||||
323 | } | ||||||
324 | |||||||
325 | 1 | 3 | return $text; | ||||
326 | } | ||||||
327 | |||||||
328 | sub _html_tag | ||||||
329 | { | ||||||
330 | 2 | 2 | 6 | my ($parser, $type, $tagname, $orig, $attr) = @_; | |||
331 | 2 | 3 | $tagname =~ s|/$||; | ||||
332 | |||||||
333 | 2 | 0 | 0 | 5 | if ($parser->in_nowiki && ($type ne 'E' || $tagname ne 'nowiki')) { | ||
33 | |||||||
334 | 0 | 0 | $parser->append_text(encode($orig)); | ||||
335 | 0 | 0 | return; | ||||
336 | } | ||||||
337 | 2 | 0 | 0 | 6 | if ($parser->in_state('pre') && ($type ne 'E' || $tagname ne 'pre')) { | ||
33 | |||||||
338 | 0 | 0 | $parser->append_text(encode($orig)); | ||||
339 | 0 | 0 | return; | ||||
340 | } | ||||||
341 | 2 | 50 | 7 | if (my $info = $parser->{tags}{$tagname}) { | |||
342 | 2 | 3 | my $tagstack = $parser->{tag_stack}; | ||||
343 | 2 | 100 | 6 | if ($type eq 'E') { | |||
344 | 1 | 50 | 11 | if ($info->{empty}) { | |||
50 | |||||||
50 | |||||||
50 | |||||||
50 | |||||||
345 | 0 | 0 | warn "empty tags"; | ||||
346 | #skip empty tags; | ||||||
347 | } elsif ($info->{nowiki}) { | ||||||
348 | # my $text = _close_to($parser, $tagname); | ||||||
349 | 0 | 0 | $parser->end_nowiki(); | ||||
350 | } elsif ($info->{block}) { | ||||||
351 | 0 | 0 | $parser->close_block(); | ||||
352 | } elsif ($info->{phrase}) { | ||||||
353 | 0 | 0 | my $text = _close_to($parser, $tagname); | ||||
354 | 0 | 0 | $parser->append_text($text); | ||||
355 | } elsif ($info->{special}) { | ||||||
356 | 1 | 3 | $parser->close_block(); | ||||
357 | 1 | 4 | my $text = _close_to($parser, $tagname); | ||||
358 | 1 | 3 | $parser->add_block($text); | ||||
359 | } else { | ||||||
360 | 0 | 0 | die "helpme $tagname"; | ||||
361 | } | ||||||
362 | } else { | ||||||
363 | 1 | 50 | 11 | if ($info->{empty}) { | |||
50 | |||||||
50 | |||||||
50 | |||||||
50 | |||||||
364 | 0 | 0 | $parser->append_text("<$tagname/>"); | ||||
365 | } elsif ($info->{nowiki}) { | ||||||
366 | 0 | 0 | $parser->start_nowiki(); | ||||
367 | # push @$tagstack, $tagname; | ||||||
368 | } elsif (my $blockname = $info->{block}) { | ||||||
369 | 0 | 0 | $parser->close_block( new_state => $blockname ); | ||||
370 | |||||||
371 | # $parser->{state} = $blockname; | ||||||
372 | 0 | 0 | 0 | unless ($info->{notag}) { | |||
373 | 0 | 0 | $parser->append_text("<$tagname>"); | ||||
374 | } | ||||||
375 | 0 | 0 | push @$tagstack, $tagname; | ||||
376 | } elsif ($info->{phrase}) { | ||||||
377 | 0 | 0 | push(@$tagstack, $tagname); | ||||
378 | 0 | 0 | my $text = "<$tagname>"; | ||||
379 | 0 | 0 | $parser->append_text($text); | ||||
380 | } elsif ($info->{special}) { | ||||||
381 | 1 | 12 | $parser->close_block(); | ||||
382 | 1 | 2 | push(@$tagstack, $tagname); | ||||
383 | 1 | 3 | my $text = "<$tagname>"; | ||||
384 | 1 | 3 | $parser->add_block($text); | ||||
385 | } else { | ||||||
386 | 0 | 0 | die "helpme $tagname"; | ||||
387 | 0 | 0 | push @$tagstack, $tagname; | ||||
388 | } | ||||||
389 | } | ||||||
390 | } else { | ||||||
391 | 0 | 0 | $parser->append_text($parser, encode($orig)); | ||||
392 | } | ||||||
393 | |||||||
394 | 2 | 6 | return; | ||||
395 | } | ||||||
396 | |||||||
397 | sub _html_comment | ||||||
398 | 0 | 0 | 0 | { | |||
399 | # warn "_html_comment: " . join(' ', @_); | ||||||
400 | } | ||||||
401 | |||||||
402 | sub _html_text | ||||||
403 | { | ||||||
404 | 13 | 13 | 25 | my ($parser, $dtext, $skipped_text, $is_cdata) = @_; | |||
405 | 13 | 14 | my @tagstack = @{$parser->{tag_stack}}; | ||||
13 | 29 | ||||||
406 | 13 | 14 | my ($newtext, $newstate); | ||||
407 | |||||||
408 | 13 | 100 | 48 | if (my ($leading) = ($dtext =~ /^(\n+)/m)) { | |||
409 | 11 | 18 | my $x = length($leading); | ||||
410 | 11 | 20 | $parser->end_line($x); | ||||
411 | 11 | 21 | $dtext = substr($dtext, $x); | ||||
412 | } | ||||||
413 | |||||||
414 | 13 | 50 | 33 | 32 | if ($is_cdata && $parser->can_cdata) { | ||
415 | 0 | 0 | $newtext = $dtext; | ||||
416 | } else { | ||||||
417 | 13 | 22 | $newtext = encode($dtext); | ||||
418 | } | ||||||
419 | |||||||
420 | 13 | 28 | $parser->append_text($newtext); | ||||
421 | |||||||
422 | # warn "Got skipped_text: `$skipped_text'\n[$dtext]\n" if $skipped_text; | ||||||
423 | } | ||||||
424 | |||||||
425 | { | ||||||
426 | package XHTML::MediaWiki::Parser; | ||||||
427 | |||||||
428 | 2 | 2 | 31 | use base 'HTML::Parser'; | |||
2 | 3 | ||||||
2 | 358 | ||||||
429 | |||||||
430 | 2 | 2 | 12 | use Params::Validate qw (validate); | |||
2 | 3 | ||||||
2 | 7900 | ||||||
431 | |||||||
432 | sub can_cdata | ||||||
433 | { | ||||||
434 | 0 | 0 | 0 | my $self = shift; | |||
435 | 0 | 0 | 0 | if (my $current = $self->check_current_block) { | |||
436 | 0 | 0 | return $self->{tags}{$current->{type}}{can_cdata}; | ||||
437 | } | ||||||
438 | 0 | 0 | return 0; | ||||
439 | } | ||||||
440 | |||||||
441 | sub end_line | ||||||
442 | { | ||||||
443 | 11 | 11 | 12 | my $self = shift; | |||
444 | |||||||
445 | 11 | 19 | my $block = $self->get_last_line_block; | ||||
446 | |||||||
447 | 11 | 23 | $block->set_end_line(@_);; | ||||
448 | } | ||||||
449 | |||||||
450 | sub state | ||||||
451 | { | ||||||
452 | 13 | 13 | 14 | my $self = shift; | |||
453 | |||||||
454 | 13 | 22 | my $block = $self->check_current_block; | ||||
455 | 13 | 100 | 29 | return "none" unless $block; | |||
456 | 10 | 26 | return $block->get_state; | ||||
457 | } | ||||||
458 | |||||||
459 | sub in_state | ||||||
460 | { | ||||||
461 | 13 | 13 | 13 | my $self = shift; | |||
462 | 13 | 14 | my $state = shift; | ||||
463 | 13 | 50 | 27 | die if @_; | |||
464 | 13 | 21 | my $cstate = $self->state; | ||||
465 | |||||||
466 | 13 | 50 | 79 | $cstate && $cstate eq $state; | |||
467 | } | ||||||
468 | |||||||
469 | sub in_paragraph | ||||||
470 | { | ||||||
471 | 0 | 0 | 0 | my $self = shift; | |||
472 | 0 | 0 | my $ret = 0; | ||||
473 | 0 | 0 | 0 | if (my $block = $self->check_current_block) { | |||
474 | 0 | 0 | $ret = $block->is_paragraph; | ||||
475 | } | ||||||
476 | 0 | 0 | return $ret; | ||||
477 | } | ||||||
478 | |||||||
479 | sub in_prewiki | ||||||
480 | { | ||||||
481 | 11 | 11 | 12 | my $self = shift; | |||
482 | 11 | 11 | my $ret = 0; | ||||
483 | 11 | 100 | 20 | if (my $block = $self->check_current_block) { | |||
484 | 8 | 14 | $ret = $block->is_prewiki; | ||||
485 | } | ||||||
486 | 11 | 30 | return $ret; | ||||
487 | } | ||||||
488 | |||||||
489 | sub noformat | ||||||
490 | { | ||||||
491 | 11 | 11 | 12 | my $self = shift; | |||
492 | |||||||
493 | 11 | 50 | 20 | $self->in_state('pre') or $self->in_nowiki(); | |||
494 | } | ||||||
495 | |||||||
496 | sub add_block | ||||||
497 | { | ||||||
498 | 2 | 2 | 2 | my $self = shift; | |||
499 | |||||||
500 | 2 | 50 | 6 | if ($self->{current_block}) { | |||
501 | 0 | 0 | push(@{$self->{blocks}}, | ||||
0 | 0 | ||||||
502 | $self->{current_block} | ||||||
503 | ); | ||||||
504 | 0 | 0 | die "This should have been handled by close_block"; | ||||
505 | } | ||||||
506 | 2 | 6 | my $block = $self->{current_block} = XHTML::MediaWiki::Parser::Block->new(type => 'special'); | ||||
507 | 2 | 9 | $block->append_text(join('', @_)); | ||||
508 | 2 | 3 | push(@{$self->{blocks}}, | ||||
2 | 5 | ||||||
509 | $self->{current_block} | ||||||
510 | ); | ||||||
511 | 2 | 7 | $self->{current_block} = undef; | ||||
512 | } | ||||||
513 | |||||||
514 | sub close_block | ||||||
515 | { | ||||||
516 | 3 | 3 | 5 | my $self = shift; | |||
517 | 3 | 64 | my %p = validate(@_, { | ||||
518 | new_state => { | ||||||
519 | optional => 1, | ||||||
520 | }, | ||||||
521 | indent => { | ||||||
522 | optional => 1, | ||||||
523 | }, | ||||||
524 | auto_merge => { | ||||||
525 | optional => 1, | ||||||
526 | }, | ||||||
527 | }); | ||||||
528 | |||||||
529 | 3 | 15 | my $tagstack = $self->{tag_stack}; | ||||
530 | 3 | 4 | my $find = undef; | ||||
531 | 3 | 4 | my $text = ''; | ||||
532 | 3 | 50 | 6 | if (!@$tagstack) { | |||
533 | # nothing to close; | ||||||
534 | } else { | ||||||
535 | 3 | 5 | for my $tagname (@$tagstack) { | ||||
536 | 4 | 8 | my $info = $self->{tags}{$tagname}; | ||||
537 | 4 | 100 | 10 | if ($info->{block}) { | |||
538 | 3 | 7 | $find = $tagname; | ||||
539 | } | ||||||
540 | } | ||||||
541 | } | ||||||
542 | 3 | 50 | 8 | if ($find) { | |||
543 | 3 | 7 | $text = $self->close_to($find); | ||||
544 | 3 | 50 | 13 | if ($text) { | |||
545 | 0 | 0 | $self->append_text($text); | ||||
546 | } | ||||||
547 | } | ||||||
548 | |||||||
549 | 3 | 50 | 7 | if (my $current = $self->{current_block}) { | |||
0 | |||||||
550 | 3 | 50 | 33 | 9 | if ($p{auto_merge} && $p{new_state} eq $self->{current_block}->block_type) { | ||
551 | 0 | 0 | 0 | push(@{$current->{lines}}, $current->{line}) if ($current->{line}); | |||
0 | 0 | ||||||
552 | 0 | 0 | $current->{line} = undef; | ||||
553 | } else { | ||||||
554 | 3 | 4 | push(@{$self->{blocks}}, | ||||
3 | 7 | ||||||
555 | $self->{current_block} | ||||||
556 | ); | ||||||
557 | 3 | 6 | $self->{current_block} = undef; | ||||
558 | 3 | 50 | 8 | if (my $state = $p{new_state}) { | |||
559 | 0 | 0 | 0 | 0 | if ($state eq 'ordered' || $state eq 'unordered') { | ||
560 | 0 | 0 | 0 | die "Need indent" unless exists $p{indent}; | |||
561 | 0 | 0 | $self->{indent} = $p{indent}; | ||||
562 | } | ||||||
563 | 0 | 0 | $self->{current_block} = XHTML::MediaWiki::Parser::Block->new( | ||||
564 | type => $state, | ||||||
565 | level => $p{indent}, | ||||||
566 | ); | ||||||
567 | } | ||||||
568 | } | ||||||
569 | } elsif (my $state = $p{new_state}) { | ||||||
570 | 0 | 0 | $self->{current_block} = XHTML::MediaWiki::Parser::Block->new( | ||||
571 | type => $state, | ||||||
572 | level => $p{indent}, | ||||||
573 | ); | ||||||
574 | } | ||||||
575 | |||||||
576 | 3 | 7 | return $self; | ||||
577 | } | ||||||
578 | |||||||
579 | sub close_to | ||||||
580 | { | ||||||
581 | 3 | 3 | 5 | my $parser = shift; | |||
582 | 3 | 4 | my $tag = shift; | ||||
583 | 3 | 4 | my $tagstack = $parser->{tag_stack}; | ||||
584 | 3 | 4 | my $text = ''; | ||||
585 | |||||||
586 | 3 | 50 | 6 | if (!@$tagstack) { | |||
587 | 0 | 0 | 0 | $text .= "" if $DEBUG; | |||
588 | # ignore extra closing tags | ||||||
589 | } else { | ||||||
590 | 3 | 7 | while (my $toptag = pop @$tagstack) { | ||||
591 | 3 | 50 | 10 | if (! $parser->{tags}{$toptag}{notag}) { | |||
592 | 0 | 0 | $text .= "$toptag>"; | ||||
593 | } | ||||||
594 | 3 | 50 | 8 | last if $tag eq $toptag; | |||
595 | } | ||||||
596 | } | ||||||
597 | 3 | 7 | return $text; | ||||
598 | } | ||||||
599 | |||||||
600 | sub start_nowiki | ||||||
601 | { | ||||||
602 | 0 | 0 | 0 | my $self = shift; | |||
603 | 0 | 0 | my $block = $self->get_current_block; | ||||
604 | |||||||
605 | 0 | 0 | $block->set_nowiki; | ||||
606 | } | ||||||
607 | |||||||
608 | sub end_nowiki | ||||||
609 | { | ||||||
610 | 0 | 0 | 0 | my $self = shift; | |||
611 | 0 | 0 | my $block = $self->get_current_block; | ||||
612 | |||||||
613 | 0 | 0 | $block->set_wiki; | ||||
614 | } | ||||||
615 | |||||||
616 | sub in_nowiki | ||||||
617 | { | ||||||
618 | 13 | 13 | 14 | my $self = shift; | |||
619 | |||||||
620 | 13 | 100 | 21 | if (my $block = $self->check_current_block) { | |||
621 | 10 | 25 | return $block->in_nowiki; | ||||
622 | } else { | ||||||
623 | 3 | 11 | return 0; | ||||
624 | } | ||||||
625 | } | ||||||
626 | |||||||
627 | sub check_current_block | ||||||
628 | { | ||||||
629 | 37 | 37 | 37 | my $self = shift; | |||
630 | |||||||
631 | 37 | 78 | $self->{current_block}; | ||||
632 | } | ||||||
633 | |||||||
634 | sub get_current_block | ||||||
635 | { | ||||||
636 | 24 | 24 | 25 | my $self = shift; | |||
637 | |||||||
638 | 24 | 100 | 55 | if (!$self->{current_block}) { | |||
639 | 4 | 6 | my $tagstack = $self->{tag_stack}; | ||||
640 | 4 | 50 | 18 | my $new_state = $self->{state} || 'paragraph'; | |||
641 | 4 | 6 | delete $self->{state}; | ||||
642 | 4 | 50 | 10 | croak() if $new_state eq 'unordered'; | |||
643 | 4 | 14 | $self->{current_block} = XHTML::MediaWiki::Parser::Block->new(type => $new_state); | ||||
644 | 4 | 5 | push @{$self->{tag_stack}}, 'paragraph'; | ||||
4 | 10 | ||||||
645 | } | ||||||
646 | 24 | 41 | return $self->{current_block}; | ||||
647 | } | ||||||
648 | |||||||
649 | sub get_last_line_block | ||||||
650 | { | ||||||
651 | 11 | 11 | 12 | my $self = shift; | |||
652 | 11 | 14 | my $block = $self->get_current_block; | ||||
653 | |||||||
654 | 11 | 50 | 23 | if (! defined $block) { | |||
655 | 0 | 0 | $block = $self->{blocks}[-1]; | ||||
656 | } | ||||||
657 | 11 | 14 | return $block; | ||||
658 | } | ||||||
659 | |||||||
660 | sub append_text | ||||||
661 | { | ||||||
662 | 13 | 13 | 14 | my $self = shift; | |||
663 | 13 | 14 | my $text = shift; | ||||
664 | |||||||
665 | 13 | 22 | my $block = $self->get_current_block; | ||||
666 | |||||||
667 | 13 | 26 | $block->append_text($text); | ||||
668 | } | ||||||
669 | |||||||
670 | sub get_blocks | ||||||
671 | { | ||||||
672 | 1 | 1 | 2 | my $self = shift; | |||
673 | 1 | 1 | my @blocks; | ||||
674 | |||||||
675 | 1 | 2 | for my $block (@{$self->{blocks}}) { | ||||
1 | 3 | ||||||
676 | 5 | 50 | 12 | next unless $block; | |||
677 | 5 | 50 | 66 | 16 | if ($block->{type} eq 'paragraph' && 0 == @{$block->{lines}} && !$block->{line}) { | ||
3 | 66 | 19 | |||||
678 | 0 | 0 | warn "fix"; | ||||
679 | 0 | 0 | next; | ||||
680 | } | ||||||
681 | 5 | 11 | push @blocks, $block; | ||||
682 | } | ||||||
683 | 1 | 4 | @blocks; | ||||
684 | } | ||||||
685 | |||||||
686 | sub eof | ||||||
687 | { | ||||||
688 | 1 | 1 | 2 | my $self = shift; | |||
689 | 1 | 3 | $self->close_block(); | ||||
690 | 1 | 2 | for my $tag (@{$self->{tag_stack}}) { | ||||
1 | 3 | ||||||
691 | 0 | 0 | $self->append_text("$tag>\n"); | ||||
692 | } | ||||||
693 | 1 | 15 | $self->SUPER::eof(@_); | ||||
694 | } | ||||||
695 | } | ||||||
696 | |||||||
697 | sub _find_blocks_in_html | ||||||
698 | { | ||||||
699 | 1 | 1 | 2 | my $self = shift; | |||
700 | 1 | 50 | 5 | my $text = shift || ""; | |||
701 | 1 | 50 | 4 | die if @_; | |||
702 | |||||||
703 | 1 | 22 | my $parser = XHTML::MediaWiki::Parser->new | ||||
704 | (start_h => [\&_html_tag, 'self, "S", tagname, text, attr'], | ||||||
705 | end_h => [\&_html_tag, 'self, "E", tagname, text'], | ||||||
706 | comment_h => [\&_html_comment, 'self, text'], | ||||||
707 | text_h => [\&_html_text, 'self, dtext, skipped_text, is_cdata'], | ||||||
708 | marked_sections => 1, | ||||||
709 | boolean_attribute_value => '__TEXT_MEDIAWIKIFORMAT_BOOL__', | ||||||
710 | ); | ||||||
711 | 1 | 174 | $parser->{opts} = {}, | ||||
712 | $parser->{tags} = { | ||||||
713 | b => { phrase => 1 }, | ||||||
714 | big => { phrase => 1 }, | ||||||
715 | blockquote => { phrase => 1 }, | ||||||
716 | br => { empty => 1 }, | ||||||
717 | caption => {}, | ||||||
718 | center => {}, | ||||||
719 | cite => {}, | ||||||
720 | code => { phrase => 1 }, | ||||||
721 | dd => {}, | ||||||
722 | div => { | ||||||
723 | special => 1, | ||||||
724 | }, | ||||||
725 | dl => {}, | ||||||
726 | dt => {}, | ||||||
727 | em => {}, | ||||||
728 | font => {}, | ||||||
729 | |||||||
730 | h1 => { block => 'header' }, | ||||||
731 | h2 => { block => 'header' }, | ||||||
732 | h3 => { block => 'header' }, | ||||||
733 | h4 => { block => 'header' }, | ||||||
734 | h5 => { block => 'header' }, | ||||||
735 | h6 => { block => 'header' }, | ||||||
736 | |||||||
737 | hr => { empty => 1 }, | ||||||
738 | i => { }, | ||||||
739 | li => { }, | ||||||
740 | nowiki => { | ||||||
741 | nowiki => 1, | ||||||
742 | notag => 1, | ||||||
743 | }, | ||||||
744 | ol => { }, | ||||||
745 | p => { block => 'p' }, | ||||||
746 | paragraph => { | ||||||
747 | block => 'paragraph', | ||||||
748 | notag => 1 | ||||||
749 | }, | ||||||
750 | pre => { | ||||||
751 | block => 'pre', | ||||||
752 | # nowiki => 1, | ||||||
753 | }, | ||||||
754 | rb => {}, | ||||||
755 | rp => {}, | ||||||
756 | rt => {}, | ||||||
757 | ruby => { | ||||||
758 | block => 'ruby', | ||||||
759 | can_cdata => 1, | ||||||
760 | }, | ||||||
761 | s => {}, | ||||||
762 | samp => {}, | ||||||
763 | small => {}, | ||||||
764 | strike => {}, | ||||||
765 | strong => {}, | ||||||
766 | sub => {}, | ||||||
767 | sup => {}, | ||||||
768 | table => {}, | ||||||
769 | td => {}, | ||||||
770 | th => {}, | ||||||
771 | tr => {}, | ||||||
772 | tt => {}, | ||||||
773 | u => {}, | ||||||
774 | ul => {}, | ||||||
775 | var => {}, | ||||||
776 | }; | ||||||
777 | 1 | 5 | $parser->{tag_stack} = []; | ||||
778 | 1 | 3 | $parser->{blocks} = []; | ||||
779 | 1 | 2 | $parser->{current_block} = undef; | ||||
780 | |||||||
781 | 1 | 16 | my @lines = split(/\r?\n/, $text); | ||||
782 | |||||||
783 | 1 | 3 | for my $line (@lines) { | ||||
784 | 11 | 12 | my $close = 0; | ||||
785 | 11 | 50 | 27 | die if chomp $line; | |||
786 | 11 | 50 | 22 | if ($parser->noformat) { | |||
787 | # we are in nowiki or pre block | ||||||
788 | } else { | ||||||
789 | 11 | 0 | 33 | 22 | if ($parser->in_prewiki && $line && $line !~ m/^\s+/) { | ||
33 | |||||||
790 | 0 | 0 | $parser->close_block(); | ||||
791 | } | ||||||
792 | 11 | 50 | 91 | if ($line =~ qr/^(={1,6})\s*(.+?)\s*\1$/) { | |||
50 | |||||||
50 | |||||||
50 | |||||||
50 | |||||||
793 | 0 | 0 | my $x = length $1; | ||||
794 | 0 | 0 | $line = sprintf(" |
||||
795 | 0 | 0 | $parser->{last} = 'header'; | ||||
796 | } elsif ($line =~ /^$/) { | ||||||
797 | 0 | 0 | 0 | if ($parser->check_current_block) { | |||
798 | 0 | 0 | 0 | if ($parser->in_paragraph) { | |||
0 | |||||||
799 | 0 | 0 | $parser->close_block(); | ||||
800 | } elsif ($parser->in_prewiki) { | ||||||
801 | 0 | 0 | $parser->close_block(); | ||||
802 | } else { | ||||||
803 | } | ||||||
804 | } else { | ||||||
805 | 0 | 0 | 0 | 0 | unless ({header => 1, prewiki => 1}->{$parser->{last} || ''}) { | ||
806 | 0 | 0 | $line = " "; |
||||
807 | } | ||||||
808 | } | ||||||
809 | } elsif ($line =~ m/^\s(\s*.*)$/) { | ||||||
810 | 0 | 0 | $line = $1; | ||||
811 | 0 | 0 | $parser->close_block( new_state => 'prewiki', auto_merge => 1 ); | ||||
812 | |||||||
813 | 0 | 0 | $parser->{last} = 'prewiki'; | ||||
814 | } elsif ($line =~ m/^(#+)\s*(.*)\s*$/) { | ||||||
815 | 0 | 0 | my $x = length $1; | ||||
816 | 0 | 0 | $parser->close_block( new_state => 'ordered', indent => $x ); | ||||
817 | 0 | 0 | $close = 1; | ||||
818 | 0 | 0 | $line = $2; | ||||
819 | 0 | 0 | $parser->{last} = 'nested'; | ||||
820 | } elsif ($line =~ m/^(\*+)\s*(.*)\s*$/) { | ||||||
821 | 0 | 0 | my $x = length $1; | ||||
822 | 0 | 0 | $parser->close_block( new_state => 'unordered', indent => $x ); | ||||
823 | 0 | 0 | $close = 1; | ||||
824 | 0 | 0 | $line = $2; | ||||
825 | 0 | 0 | $parser->{last} = 'nested'; | ||||
826 | } else { | ||||||
827 | } | ||||||
828 | } | ||||||
829 | 11 | 50 | 34 | next unless $line; | |||
830 | 11 | 51 | $parser->parse($line); | ||||
831 | 11 | 46 | $parser->parse("\n"); | ||||
832 | |||||||
833 | 11 | 14 | $parser->{empty_lines} = 0; | ||||
834 | |||||||
835 | 11 | 50 | 26 | $parser->close_block() if $close; | |||
836 | } | ||||||
837 | 1 | 4 | $parser->eof(); | ||||
838 | 1 | 1 | my @blocks; | ||||
839 | |||||||
840 | 1 | 5 | for my $block ($parser->get_blocks) { | ||||
841 | 5 | 50 | 11 | next unless defined $block; | |||
842 | 5 | 12 | my $type = $block->block_type; | ||||
843 | 5 | 13 | my $class = $self->get_block($type); | ||||
844 | |||||||
845 | 5 | 13 | my $new_block = | ||||
846 | $class->new ( | ||||||
847 | type => $type, | ||||||
848 | $block->args, | ||||||
849 | formater => $self, | ||||||
850 | ); | ||||||
851 | 5 | 15 | push @blocks, $new_block; | ||||
852 | } | ||||||
853 | |||||||
854 | 1 | 39 | return @blocks; | ||||
855 | } | ||||||
856 | |||||||
857 | sub _find_blocks | ||||||
858 | { | ||||||
859 | 1 | 1 | 1 | my $self = shift; | |||
860 | 1 | 2 | my $text = shift; | ||||
861 | |||||||
862 | 1 | 2 | my @blocks; | ||||
863 | |||||||
864 | 1 | 4 | @blocks = $self->_find_blocks_in_html($text); | ||||
865 | |||||||
866 | 1 | 4 | return @blocks; | ||||
867 | } | ||||||
868 | |||||||
869 | sub _nest_blocks | ||||||
870 | { | ||||||
871 | 1 | 1 | 2 | my $self = shift; | |||
872 | 1 | 2 | my @blocks = @_; | ||||
873 | 1 | 50 | 4 | return unless @blocks; | |||
874 | |||||||
875 | 1 | 3 | my @processed = shift @blocks; | ||||
876 | 1 | 2 | for my $block (@blocks) | ||||
877 | { | ||||||
878 | 4 | 29 | my @x = $processed[-1]->nest( $block ); | ||||
879 | 4 | 10 | push @processed, @x; | ||||
880 | } | ||||||
881 | |||||||
882 | 1 | 5 | return @processed; | ||||
883 | } | ||||||
884 | |||||||
885 | sub _process_blocks | ||||||
886 | { | ||||||
887 | 1 | 1 | 1 | my $self = shift; | |||
888 | 1 | 3 | my @blocks = @_; | ||||
889 | 1 | 3 | my @open; | ||||
890 | 1 | 2 | for my $block (@blocks) | ||||
891 | { | ||||||
892 | 5 | 13 | push @open, $self->_process_block($block); | ||||
893 | } | ||||||
894 | 1 | 5 | return join '', @open ; | ||||
895 | } | ||||||
896 | |||||||
897 | sub _process_block | ||||||
898 | { | ||||||
899 | 5 | 5 | 7 | my $self = shift; | |||
900 | 5 | 6 | my ($block, $tags, $opts) = @_; | ||||
901 | 5 | 18 | my $type = $block->type(); | ||||
902 | |||||||
903 | 5 | 8 | my ($start, $end, $start_line, $end_line, $between); | ||||
904 | 5 | 50 | 12 | if ($tags->{$type}) | |||
905 | { | ||||||
906 | 0 | 0 | ($start, $end, $start_line, $end_line, $between) = @{$tags->{$type}}; | ||||
0 | 0 | ||||||
907 | } | ||||||
908 | else | ||||||
909 | { | ||||||
910 | 5 | 10 | ($start, $end, $start_line, $end_line) = ('', '', '', ''); | ||||
911 | } | ||||||
912 | |||||||
913 | 5 | 7 | my @text = (); | ||||
914 | 5 | 50 | 7 | for my $line (grep (/^\Q$type\E$/, @{$tags->{unformatted_blocks}}) | |||
5 | 25 | ||||||
915 | ? $block->text() | ||||||
916 | : $block->formatted_text()) | ||||||
917 | { | ||||||
918 | 5 | 50 | 16 | if (blessed $line) | |||
919 | { | ||||||
920 | 0 | 0 | 0 | my $prev_end = pop @text || (); | |||
921 | 0 | 0 | push @text, _process_block ($line, $tags, $opts), $prev_end; | ||||
922 | 0 | 0 | next; | ||||
923 | } | ||||||
924 | |||||||
925 | 5 | 6 | my @triplets; | ||||
926 | 5 | 50 | 50 | 22 | if ((ref ($start_line) || '') eq 'CODE') | ||
927 | { | ||||||
928 | 0 | 0 | @triplets = $start_line->($line, $block->level(), | ||||
929 | $block->shift_args(), $tags, $opts); | ||||||
930 | } | ||||||
931 | else | ||||||
932 | { | ||||||
933 | 5 | 11 | @triplets = ($start_line, $line, $end_line); | ||||
934 | } | ||||||
935 | 5 | 16 | push @text, @triplets; | ||||
936 | } | ||||||
937 | |||||||
938 | 5 | 50 | 13 | pop @text if $between; | |||
939 | 5 | 26 | return join '', $start, @text, $end; | ||||
940 | } | ||||||
941 | |||||||
942 | sub _format | ||||||
943 | { | ||||||
944 | 1 | 1 | 2 | my $self = shift; | |||
945 | 1 | 2 | my $text = shift; | ||||
946 | |||||||
947 | 1 | 5 | my @blocks = $self->_find_blocks($text); | ||||
948 | |||||||
949 | 1 | 6 | @blocks = $self->_nest_blocks(@blocks); | ||||
950 | 1 | 6 | my $ret = $self->_process_blocks(@blocks); | ||||
951 | |||||||
952 | 1 | 23 | return $ret; | ||||
953 | } | ||||||
954 | |||||||
955 | sub _strong | ||||||
956 | { | ||||||
957 | 1 | 1 | 10 | "$_[1]"; | |||
958 | } | ||||||
959 | |||||||
960 | =item * emphasized() | ||||||
961 | |||||||
962 | emphasized controls the output of "" tags. | ||||||
963 | |||||||
964 | =cut | ||||||
965 | |||||||
966 | sub emphasized | ||||||
967 | { | ||||||
968 | 1 | 1 | 1 | 5 | "$_[1]"; | ||
969 | } | ||||||
970 | |||||||
971 | =item * link() | ||||||
972 | |||||||
973 | The link method is often overridden to modify the display and | ||||||
974 | operation of links. | ||||||
975 | |||||||
976 | link takes 3 arguments the Link, any extra_text, and the type of the link; | ||||||
977 | |||||||
978 | The type is true for footnotes. | ||||||
979 | |||||||
980 | =cut | ||||||
981 | |||||||
982 | sub link | ||||||
983 | { | ||||||
984 | 3 | 3 | 1 | 6 | my $self = shift; | ||
985 | 3 | 50 | 17 | my $link = shift || ''; | |||
986 | 3 | 50 | 45 | my $extra = shift || ''; | |||
987 | 3 | 7 | my $type = shift; | ||||
988 | 3 | 5 | my $text = $link; | ||||
989 | 3 | 100 | 10 | if ($type) { | |||
990 | 1 | 3 | $text = ++$self->{footnote}; | ||||
991 | } else { | ||||||
992 | 2 | 11 | $link = $self->{link_path} . $link; | ||||
993 | } | ||||||
994 | 3 | 22 | qq|$text$extra|; | ||||
995 | } | ||||||
996 | |||||||
997 | =item * find_links() | ||||||
998 | |||||||
999 | The C |
||||||
1000 | links are detected. | ||||||
1001 | |||||||
1002 | =cut | ||||||
1003 | |||||||
1004 | sub find_links | ||||||
1005 | { | ||||||
1006 | 7 | 7 | 1 | 12 | my $self = shift; | ||
1007 | 7 | 21 | my $text = shift; | ||||
1008 | |||||||
1009 | 7 | 50 | 39 | return '' unless defined $text; | |||
1010 | |||||||
1011 | 7 | 26 | $text =~ s/\[\[([^\]]*)\]\]([A-Za-z0-9]*)/$self->link($1, $2, 0)/ge; | ||||
2 | 8 | ||||||
1012 | 7 | 16 | $text =~ s/\[([a-zA-Z]+:[^\]]*)\]/$self->link($1, '', 1)/ge; | ||||
1 | 5 | ||||||
1013 | |||||||
1014 | 7 | 19 | return $text; | ||||
1015 | } | ||||||
1016 | |||||||
1017 | =item * template_text() | ||||||
1018 | |||||||
1019 | Override this method to control the text that is generated for an unknown template ({{word}}). | ||||||
1020 | |||||||
1021 | =cut | ||||||
1022 | |||||||
1023 | sub template_text | ||||||
1024 | { | ||||||
1025 | 5 | 5 | 1 | 6 | my $self = shift; | ||
1026 | 5 | 11 | my $text = shift; | ||||
1027 | 5 | 50 | 16 | die if @_; | |||
1028 | 5 | 26 | 'No template for: ' . $text . ''; | ||||
1029 | } | ||||||
1030 | |||||||
1031 | =item * format_line() | ||||||
1032 | |||||||
1033 | Override this method to extend or modify line level parsing. | ||||||
1034 | |||||||
1035 | =cut | ||||||
1036 | |||||||
1037 | sub format_line | ||||||
1038 | { | ||||||
1039 | 7 | 7 | 1 | 1247 | my $self = shift; | ||
1040 | 7 | 13 | my $text = shift; | ||||
1041 | |||||||
1042 | 7 | 50 | 46 | return '' unless defined $text; | |||
1043 | |||||||
1044 | 7 | 36 | my $strong_tag = qr/'''(.+?)'''/; | ||||
1045 | 7 | 20 | my $emphasized_tag = qr/''(.+?)''/; | ||||
1046 | |||||||
1047 | 7 | 35 | $text =~ s!$strong_tag!$self->_strong($1)!eg; | ||||
1 | 5 | ||||||
1048 | 7 | 23 | $text =~ s!$emphasized_tag!$self->emphasized($1)!eg; | ||||
1 | 6 | ||||||
1049 | |||||||
1050 | 7 | 21 | $text = $self->find_links($text); | ||||
1051 | |||||||
1052 | 7 | 24 | my $template_tag = qr/{{\s*([a-zA-Z0-9][a-z0-9|]*)\s*}}/; | ||||
1053 | 7 | 35 | $text =~ s!$template_tag!$self->template_text($1)!eg; | ||||
5 | 12 | ||||||
1054 | |||||||
1055 | 7 | 40 | return $text; | ||||
1056 | } | ||||||
1057 | |||||||
1058 | # BLOCK code is below here and needs to be moved. | ||||||
1059 | |||||||
1060 | { | ||||||
1061 | package XHTML::MediaWiki::Block::Start; | ||||||
1062 | |||||||
1063 | 2 | 2 | 18 | use base "XHTML::MediaWiki::Block"; | |||
2 | 4 | ||||||
2 | 1474 | ||||||
1064 | sub formatted_text | ||||||
1065 | { | ||||||
1066 | 0 | 0 | 0 | "\n"; | |||
1067 | } | ||||||
1068 | } | ||||||
1069 | { | ||||||
1070 | package XHTML::MediaWiki::Block::Header; | ||||||
1071 | |||||||
1072 | 2 | 2 | 12 | use base "XHTML::MediaWiki::Block"; | |||
2 | 4 | ||||||
2 | 22490 | ||||||
1073 | |||||||
1074 | sub formatted_text | ||||||
1075 | { | ||||||
1076 | 0 | 0 | 0 | my $self = shift; | |||
1077 | 0 | 0 | my $formatter = $self->formatter; | ||||
1078 | 0 | 0 | my $text = $self->SUPER::formatted_text(); | ||||
1079 | |||||||
1080 | 0 | 0 | my $newtext = $text; | ||||
1081 | 0 | 0 | $newtext =~ s/<[^>]+>//g; | ||||
1082 | 0 | 0 | $newtext =~ s/\s/_/g; | ||||
1083 | 0 | 0 | qq|| . $text; | ||||
1084 | } | ||||||
1085 | } | ||||||
1086 | |||||||
1087 | { | ||||||
1088 | package XHTML::MediaWiki::Block::Special; | ||||||
1089 | 2 | 2 | 22 | use base "XHTML::MediaWiki::Block"; | |||
2 | 3 | ||||||
2 | 1555 | ||||||
1090 | |||||||
1091 | sub formatted_text | ||||||
1092 | { | ||||||
1093 | 2 | 2 | 3 | my $self = shift; | |||
1094 | 2 | 16 | my $formatter = $self->formatter; | ||||
1095 | 2 | 4 | my $ret_text = ''; | ||||
1096 | 2 | 3 | for my $line (@{$self->{lines}}) { | ||||
2 | 6 | ||||||
1097 | 2 | 50 | 6 | die("internal error") unless $line; | |||
1098 | |||||||
1099 | 2 | 3 | my $text .= $line->{text}; | ||||
1100 | 2 | 50 | 5 | if ($line->{state} eq 'nowiki') { | |||
1101 | 0 | 0 | $ret_text .= $text; | ||||
1102 | } else { | ||||||
1103 | 2 | 6 | $ret_text .= $formatter->format_line($text); | ||||
1104 | } | ||||||
1105 | } | ||||||
1106 | 2 | 6 | $ret_text; | ||||
1107 | } | ||||||
1108 | } | ||||||
1109 | { | ||||||
1110 | package XHTML::MediaWiki::Block::P; | ||||||
1111 | 2 | 2 | 12 | use base "XHTML::MediaWiki::Block"; | |||
2 | 5 | ||||||
2 | 1332 | ||||||
1112 | |||||||
1113 | sub formatted_text | ||||||
1114 | { | ||||||
1115 | 0 | 0 | 0 | my $self = shift; | |||
1116 | 0 | 0 | $self->SUPER::formatted_text(@_) . "\n"; | ||||
1117 | } | ||||||
1118 | } | ||||||
1119 | { | ||||||
1120 | package XHTML::MediaWiki::Block::Paragraph; | ||||||
1121 | 2 | 2 | 14 | use base "XHTML::MediaWiki::Block"; | |||
2 | 4 | ||||||
2 | 886 | ||||||
1122 | |||||||
1123 | 2 | 2 | 13 | use Carp qw(croak); | |||
2 | 4 | ||||||
2 | 312 | ||||||
1124 | |||||||
1125 | sub formatted_text | ||||||
1126 | { | ||||||
1127 | 3 | 3 | 6 | my $self = shift; | |||
1128 | 3 | 4 | my $formater = $self->{formater}; | ||||
1129 | 3 | 4 | my $ret_text = ''; | ||||
1130 | |||||||
1131 | 3 | 4 | for my $line (@{$self->{lines}}) { | ||||
3 | 6 | ||||||
1132 | 2 | 2 | 2732 | use Data::Dumper; | |||
2 | 20009 | ||||||
2 | 528 | ||||||
1133 | 3 | 50 | 14 | warn Dumper $self unless $line; | |||
1134 | 3 | 50 | 7 | die("internal error") unless $line; | |||
1135 | |||||||
1136 | 3 | 7 | my $text .= $line->{text}; | ||||
1137 | 3 | 50 | 10 | if ($line->{state} eq 'nowiki') { | |||
1138 | 0 | 0 | $ret_text .= $text; | ||||
1139 | } else { | ||||||
1140 | 3 | 8 | $ret_text .= $formater->format_line($text); | ||||
1141 | } | ||||||
1142 | } | ||||||
1143 | 3 | 50 | 12 | if ($ret_text =~ m/^\s*$/) { | |||
1144 | # return "\n"; | ||||||
1145 | } else { | ||||||
1146 | 3 | 12 | return ' ' . $ret_text . " \n"; |
||||
1147 | } | ||||||
1148 | } | ||||||
1149 | } | ||||||
1150 | |||||||
1151 | { | ||||||
1152 | package XHTML::MediaWiki::Block::Nested; | ||||||
1153 | 2 | 2 | 19 | use base "XHTML::MediaWiki::Block"; | |||
2 | 3 | ||||||
2 | 2592 | ||||||
1154 | |||||||
1155 | sub new | ||||||
1156 | { | ||||||
1157 | 0 | 0 | 0 | my $class = shift; | |||
1158 | 0 | 0 | my $self = $class->SUPER::new(@_); | ||||
1159 | |||||||
1160 | 0 | 0 | 0 | die caller unless $self->{level}; | |||
1161 | 0 | 0 | return $self; | ||||
1162 | } | ||||||
1163 | |||||||
1164 | sub formatted_text | ||||||
1165 | { | ||||||
1166 | 0 | 0 | 0 | my $self = shift; | |||
1167 | |||||||
1168 | 0 | 0 | my $formatter = $self->formatter; | ||||
1169 | 0 | 0 | my $text = $self->SUPER::formatted_text(@_); | ||||
1170 | |||||||
1171 | 0 | 0 | my $indent = $self->{level}; | ||||
1172 | 0 | 0 | my $ret = $self->start_block; | ||||
1173 | |||||||
1174 | 0 | 0 | $ret .= ' |
||||
1175 | 0 | 0 | 0 | if ($self->{block}) { | |||
1176 | 0 | 0 | $ret .= $self->{block}->formatted_text(); | ||||
1177 | } | ||||||
1178 | 0 | 0 | $ret .= "\n"; | ||||
1179 | |||||||
1180 | 0 | 0 | for my $x (@{$self->{added}}) { | ||||
0 | 0 | ||||||
1181 | 0 | 0 | $ret .= ' |
||||
1182 | 0 | 0 | 0 | if ($x->{block}) { | |||
1183 | 0 | 0 | $ret .= $x->{block}->formatted_text(); | ||||
1184 | } | ||||||
1185 | 0 | 0 | $ret .= ""; | ||||
1186 | 0 | 0 | $ret .= "\n"; | ||||
1187 | } | ||||||
1188 | 0 | 0 | $ret .= $self->end_block; | ||||
1189 | |||||||
1190 | 0 | 0 | return $ret; | ||||
1191 | } | ||||||
1192 | |||||||
1193 | sub level | ||||||
1194 | { | ||||||
1195 | 0 | 0 | 0 | my $self = shift; | |||
1196 | |||||||
1197 | 0 | 0 | return $self->{level}; | ||||
1198 | } | ||||||
1199 | |||||||
1200 | sub cmp | ||||||
1201 | { | ||||||
1202 | 0 | 0 | 0 | my $self = shift; | |||
1203 | 0 | 0 | my $cmp_block = shift; | ||||
1204 | 0 | 0 | my $ret = 0; | ||||
1205 | |||||||
1206 | 0 | 0 | 0 | 0 | if (ref($self) eq ref($cmp_block) && $self->level == $cmp_block->level) { | ||
1207 | 0 | 0 | $ret = 1; | ||||
1208 | } | ||||||
1209 | 0 | 0 | return $ret; | ||||
1210 | } | ||||||
1211 | |||||||
1212 | sub nests | ||||||
1213 | { | ||||||
1214 | 0 | 0 | 0 | 1; | |||
1215 | } | ||||||
1216 | |||||||
1217 | sub nest_block | ||||||
1218 | { | ||||||
1219 | 0 | 0 | 0 | my $self = shift; | |||
1220 | 0 | 0 | 0 | my $current = $self->{added}->[-1] || $self; | |||
1221 | 0 | 0 | for my $block (@_) { | ||||
1222 | 0 | 0 | my $index = $block->level - $self->level; | ||||
1223 | 0 | 0 | 0 | die 'internal error' if $index <= 0; | |||
1224 | 0 | 0 | 0 | if ($index == 1) { | |||
1225 | 0 | 0 | 0 | if (my $x = $current->{block}) { | |||
1226 | 0 | 0 | $x->nest($block); | ||||
1227 | } else { | ||||||
1228 | 0 | 0 | $current->{block} = $block; | ||||
1229 | } | ||||||
1230 | } else { | ||||||
1231 | 0 | 0 | 0 | $current->{block} ||= ref($block)->new( | |||
1232 | formater => $current->{formater}, | ||||||
1233 | type => $current->type, | ||||||
1234 | level => $current->level + 1, | ||||||
1235 | ); | ||||||
1236 | 0 | 0 | $current->{block}->nest($block); | ||||
1237 | } | ||||||
1238 | } | ||||||
1239 | } | ||||||
1240 | } | ||||||
1241 | |||||||
1242 | { | ||||||
1243 | package XHTML::MediaWiki::Block::Ordered; | ||||||
1244 | 2 | 2 | 30 | use base "XHTML::MediaWiki::Block::Nested"; | |||
2 | 29 | ||||||
2 | 1356 | ||||||
1245 | 0 | 0 | 0 | sub start_block { "
|
|||
1246 | 0 | 0 | 0 | sub end_block { "\n" } | |||
1247 | } | ||||||
1248 | { | ||||||
1249 | package XHTML::MediaWiki::Block::Unordered; | ||||||
1250 | 2 | 2 | 14 | use base "XHTML::MediaWiki::Block::Nested"; | |||
2 | 85 | ||||||
2 | 1437 | ||||||
1251 | 0 | 0 | 0 | sub start_block { "
|
|||
1252 | 0 | 0 | 0 | sub end_block { "\n" } | |||
1253 | } | ||||||
1254 | { | ||||||
1255 | package XHTML::MediaWiki::Block::Pre; | ||||||
1256 | 2 | 2 | 17 | use base "XHTML::MediaWiki::Block"; | |||
2 | 6 | ||||||
2 | 1399 | ||||||
1257 | |||||||
1258 | sub formatted_text { | ||||||
1259 | 0 | 0 | 0 | my $self = shift; | |||
1260 | 0 | 0 | my $text = $self->unformatted_text; | ||||
1261 | |||||||
1262 | 0 | 0 | return $text; | ||||
1263 | } | ||||||
1264 | } | ||||||
1265 | { | ||||||
1266 | package XHTML::MediaWiki::Block::Prewiki; | ||||||
1267 | 2 | 2 | 12 | use base "XHTML::MediaWiki::Block"; | |||
2 | 2 | ||||||
2 | 1346 | ||||||
1268 | |||||||
1269 | sub formatted_text | ||||||
1270 | { | ||||||
1271 | 0 | 0 | 0 | my $self = shift; | |||
1272 | 0 | 0 | my $text = $self->SUPER::formatted_text(@_); | ||||
1273 | 0 | 0 | $text =~ s/^\s*//; | ||||
1274 | |||||||
1275 | 0 | 0 | return "\n" . '' . $text . "\n"; |
||||
1276 | } | ||||||
1277 | } | ||||||
1278 | { | ||||||
1279 | package XHTML::MediaWiki::Block::Ruby; | ||||||
1280 | 2 | 2 | 12 | use base "XHTML::MediaWiki::Block"; | |||
2 | 4 | ||||||
2 | 1268 | ||||||
1281 | |||||||
1282 | sub formatted_text | ||||||
1283 | { | ||||||
1284 | 0 | 0 | 0 | my $self = shift; | |||
1285 | 0 | 0 | my $text = $self->SUPER::unformatted_text(@_); | ||||
1286 | |||||||
1287 | 0 | 0 | return "Ruby Data"; | ||||
1288 | } | ||||||
1289 | } | ||||||
1290 | { | ||||||
1291 | package XHTML::MediaWiki::Block; | ||||||
1292 | 2 | 2 | 14 | use Params::Validate qw (validate ARRAYREF); | |||
2 | 2 | ||||||
2 | 2234 | ||||||
1293 | |||||||
1294 | sub new | ||||||
1295 | { | ||||||
1296 | 5 | 5 | 8 | my $class = shift; | |||
1297 | 5 | 101 | my %p = validate(@_, { | ||||
1298 | formater => 1, | ||||||
1299 | type => 1, | ||||||
1300 | indent => 0, | ||||||
1301 | level => 0, | ||||||
1302 | lines => ARRAYREF, | ||||||
1303 | args => 0, | ||||||
1304 | }); | ||||||
1305 | |||||||
1306 | 5 | 52 | bless { %p }, $class | ||||
1307 | } | ||||||
1308 | |||||||
1309 | sub merge_block | ||||||
1310 | { | ||||||
1311 | 0 | 0 | 0 | my $self = shift; | |||
1312 | |||||||
1313 | 0 | 0 | push(@{$self->{added}}, @_); | ||||
0 | 0 | ||||||
1314 | } | ||||||
1315 | |||||||
1316 | sub cmp | ||||||
1317 | { | ||||||
1318 | 4 | 4 | 9 | 0; | |||
1319 | } | ||||||
1320 | |||||||
1321 | sub merge | ||||||
1322 | { | ||||||
1323 | 4 | 4 | 6 | my $self = shift; | |||
1324 | 4 | 5 | my @ret = @_; | ||||
1325 | |||||||
1326 | 4 | 20 | while (my $block = pop @ret) { | ||||
1327 | 4 | 50 | 14 | if ($self->cmp($block)) { | |||
1328 | 0 | 0 | $self->merge_block($block); | ||||
1329 | } else { | ||||||
1330 | 4 | 7 | push(@ret, $block); | ||||
1331 | 4 | 6 | last; | ||||
1332 | } | ||||||
1333 | } | ||||||
1334 | |||||||
1335 | 4 | 9 | @ret; | ||||
1336 | } | ||||||
1337 | |||||||
1338 | sub nests | ||||||
1339 | { | ||||||
1340 | 4 | 4 | 21 | return 0; | |||
1341 | } | ||||||
1342 | |||||||
1343 | sub nest | ||||||
1344 | { | ||||||
1345 | 4 | 4 | 6 | my $self = shift; | |||
1346 | 4 | 7 | my @next_blocks = @_; | ||||
1347 | |||||||
1348 | 4 | 14 | @next_blocks = $self->merge(@next_blocks); | ||||
1349 | 4 | 11 | while (@next_blocks) { | ||||
1350 | 4 | 4 | my $next = $next_blocks[0]; | ||||
1351 | 4 | 50 | 33 | 16 | if ($self->nests && $next->nests) { | ||
1352 | 0 | 0 | $self->nest_block(pop @next_blocks); | ||||
1353 | } else { | ||||||
1354 | 4 | 5 | last; | ||||
1355 | } | ||||||
1356 | } | ||||||
1357 | |||||||
1358 | 4 | 10 | return @next_blocks; | ||||
1359 | } | ||||||
1360 | |||||||
1361 | sub level | ||||||
1362 | { | ||||||
1363 | 0 | 0 | 0 | my $x = shift; | |||
1364 | 0 | 0 | warn $x; | ||||
1365 | 0 | 0 | 0; | ||||
1366 | } | ||||||
1367 | |||||||
1368 | sub type | ||||||
1369 | { | ||||||
1370 | 5 | 5 | 6 | my $self = shift; | |||
1371 | |||||||
1372 | 5 | 11 | $self->{type}; | ||||
1373 | } | ||||||
1374 | |||||||
1375 | sub formatter | ||||||
1376 | { | ||||||
1377 | 2 | 2 | 3 | shift->{formater}; | |||
1378 | } | ||||||
1379 | |||||||
1380 | sub unformatted_text { | ||||||
1381 | 0 | 0 | my $self = shift; | ||||
1382 | 0 | my $formater = $self->{formater}; | |||||
1383 | 0 | my $text = ''; | |||||
1384 | |||||||
1385 | 0 | for my $line (@{$self->{lines}}) { | |||||
0 | |||||||
1386 | 0 | 0 | die("internal error") unless $line; | ||||
1387 | |||||||
1388 | 0 | $text .= $line->{text}; | |||||
1389 | } | ||||||
1390 | 0 | return $text; | |||||
1391 | } | ||||||
1392 | |||||||
1393 | sub formatted_text { | ||||||
1394 | 0 | 0 | my $self = shift; | ||||
1395 | 0 | my $formater = $self->{formater}; | |||||
1396 | 0 | my $text = ''; | |||||
1397 | |||||||
1398 | 0 | for my $line (@{$self->{lines}}) { | |||||
0 | |||||||
1399 | 0 | 0 | die("internal error") unless $line; | ||||
1400 | |||||||
1401 | 0 | 0 | if ($line->{state} eq 'nowiki') { | ||||
1402 | 0 | $text .= $line->{text}; | |||||
1403 | } else { | ||||||
1404 | 0 | $text .= $formater->format_line($line->{text}); | |||||
1405 | } | ||||||
1406 | } | ||||||
1407 | 0 | return $text; | |||||
1408 | } | ||||||
1409 | } | ||||||
1410 | |||||||
1411 | 1; | ||||||
1412 | __END__ |