File Coverage

blib/lib/HTML/WikiConverter/DokuWikiFCK.pm
Criterion Covered Total %
statement 6 6 100.0
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 8 8 100.0


line stmt bran cond sub pod time code
1             package HTML::WikiConverter::DokuWikiFCK;
2              
3             #
4             #
5             # DokuWikFCK - A WikiCoverter Dialect for interfacing DokuWiki
6             # and the FCKEditor (http://www.fckeditor.net)
7             # which seeks to implement the graphic features of FCKEditor
8             #
9             # Myron Turner
10             #
11             # GNU General Public License Version 2 or later (the "GPL")
12             # http://www.gnu.org/licenses/gpl.html
13             #
14             #
15              
16 1     1   38477 use strict;
  1         3  
  1         41  
17              
18 1     1   5 use base 'HTML::WikiConverter::DokuWiki';
  1         1  
  1         1040  
19             use HTML::Element;
20             use HTML::Entities;
21             use Params::Validate ':types';
22              
23              
24             our $VERSION = '0.32';
25              
26             my $SPACEBAR_NUDGING = 0;
27             my $color_pattern = qr/
28             ([a-zA-z]+)| #colorname
29             (\#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}))| #colorvalue
30             (rgb\(([0-9]{1,3}%?,){2}[0-9]{1,3}%?\)) #rgb triplet
31             /x;
32            
33             my $font_pattern = qr//;
34             my %style_patterns = ( 'color' => \$color_pattern, 'font' => \$font_pattern );
35              
36             my $nudge_char = '·';
37            
38             my $NL_marker = '~~~';
39             my $EOL = '=~=';
40             my $code_NL = '-NLn-';
41              
42             my %_formats = ( 'b' => '**',
43             'em' => '//',
44             'i' => '//',
45             'u' => '__',
46             'ins' => '__'
47             );
48              
49             my %_format_regex = ( 'b' => qr/\*\*/,
50             'em' => qr/\/\//,
51             'i' => qr/\/\//,
52             'u' => qr/__/,
53             'ins' => qr/__/
54             );
55              
56             my %_format_esc = ( 'b' => '%%**%%',
57             'em' => '%%//%%',
58             'i' => '%%//%%',
59             'u' => '%%__%%',
60             'ins' => '%%__%%'
61             );
62              
63             my %dw_code = ( 'regex' => qr/\'\'/,
64             'esc' => "%%\'\'%%"
65             );
66              
67             my $kbd_start = '';
68             my $kbd_end = '';
69              
70              
71             sub attributes {
72             return {
73             browser => { default => 'IE5', type => SCALAR },
74             group => { default => 'ANY', type => SCALAR },
75             };
76             }
77            
78             sub new {
79             my $class = shift;
80             my $self = $class->SUPER::new(@_);
81             $self->{'strike_out'} = 0; # this prevents deletions from being paragraphed
82             $self->{'list_type'} = "";
83             $self->{'list_output'} = 0; # tells postprocess_output to clean up lists, if 1
84             $self->{'in_table'} = 0;
85             $self->{'colspan'} = "";
86             $self->{'code'} = 0;
87             $self->{'block'} = 0;
88             $self->{'share'} = 0;
89             $self->{'do_nudge'} = $SPACEBAR_NUDGING;
90              
91             if(!$self->{'do_nudge'}) {
92             $nudge_char = ' ';
93             }
94             $self->{'err'} = "NOERR\n";
95             $self->{'_fh'} = 0; # turn off debugging
96             # $self->{'_fh'} = getFH();
97             $self->{'os'} = $^O;
98             return $self;
99             }
100              
101              
102              
103              
104             sub getFH {
105             my($self) = @_;
106             local *FH;
107             # if(open(FH, '>> C:\\Windows\\Temp\\dwfckg.log')) {
108             if(open(FH, ">> /var/tmp/dwfckg.log")) {
109             return *FH;
110             }
111             $self->{'err'} = "$! \n";
112             return 0;
113             }
114              
115              
116              
117             sub rules {
118             my $self = shift;
119             my $rules = $self->SUPER::rules();
120            
121             $rules->{ 'span' } = { replace => \&_span_contents };
122             $rules->{ 'p' } = {replace => \&_p_alignment };
123             $rules->{ 'div' } = {replace => \&_p_alignment };
124             $rules->{ 'img' } = {replace => \&_image };
125             $rules->{ 'a' } = { replace => \&_link };
126             $rules->{ 'blockquote' } = { replace => \&_block };
127             $rules->{ 'pre' } = { replace => \&_code_types };
128             $rules->{ 'var' } = { start => '//', end => '//' };
129              
130             $rules->{ 'address' } = { start => '//', end => '//' };
131             $rules->{ 'strike' } = { start => '', end => '' };
132             $rules->{ 'cite' } = { start => '//', end => '//' };
133             $rules->{ 'del' } = { alias => 'strike' };
134              
135             $rules->{ 'kbd' } = { start => $kbd_start, end => $kbd_end };
136             $rules->{ 'tt' } = { start => '', end => '' };
137             $rules->{ 'samp' } = { start => '', end => '' };
138             $rules->{ 'q' } = { start => '"', end => '"' };
139             $rules->{ 'li' } = { replace => \&_li_start };
140             $rules->{ 'ul' } = { line_format => 'multi', block => 1, line_prefix => ' ',
141             end => "\n\n" },
142             $rules->{ 'ol' } = { alias => 'ul' };
143             $rules->{ 'hr' } = { replace => "$NL_marker\n----${NL_marker}\n" };
144              
145             if($self->{'do_nudge'}) {
146             $rules->{ 'indent' } = { replace => \&_indent };
147             }
148             else {
149             $rules->{ 'indent' } = { preserve => 1 };
150             }
151             $rules->{ 'dwfckg' } = { preserve => 1 };
152             $rules->{ 'header' } = { preserve => 1 };
153             $rules->{ 'td' } = { replace => \&_td_start };
154             $rules->{ 'th' } = { alias => 'td' };
155             $rules->{ 'tr' } = { start => "$NL_marker\n", line_format => 'single', end => \&_row_end };
156             for( 1..5 ) {
157             $rules->{"h$_"} = { replace => \&_header };
158             }
159             $rules->{'plugin'} = { replace => \&_plugin};
160             # $rules->{ 'table' } = { start =>"", end => "
" };
161             $rules->{ 'table' } = { replace => \&_table };
162             $rules->{'ins'} = { alias => 'u'};
163             $rules->{'b'} = { replace => \&_formats };
164             $rules->{'i'} = { replace => \&_formats };
165             $rules->{'u'} = { replace => \&_formats };
166            
167             $rules->{'sup'} = { replace => \&_sup };
168              
169             return $rules;
170            
171             }
172              
173              
174             sub _formats {
175             my($self, $node, $rules ) = @_;
176              
177             my $text = $self->get_elem_contents($node);
178              
179             # $text = $self->trim($text);
180             return "" if ! $text;
181             return "" if $text !~ /[\p{IsDigit}\p{IsAlpha}\p{IsXDigit}\\]/;
182              
183             my @count = $text =~ /\\/g;
184             if(scalar @count) {
185             my $count = scalar @count;
186             $text = "_dwfckgBACKSLASH_" x $count;
187             return $text;
188             }
189             $text =~ s/^$_format_regex{$node->tag}//;
190             $text =~ s/$_format_regex{$node->tag}$//;
191            
192             my $tag = $node->tag;
193            
194             $tag = 'b' if $tag eq 'strong';
195            
196             return ("_<". $tag . "_>". $text . "_<" . $tag . "_>");
197            
198             }
199              
200             sub _plugin {
201             my($self, $node, $rules ) = @_;
202             my $text = $self->get_elem_contents($node); # text is the plugin pattern
203            
204             $text = $self->trim($text);
205             return "" if !$text;
206              
207             my $title = $node->attr('title');
208             $title=$self->trim($title);
209             if(!$title) {
210             return "";
211             }
212              
213             $text =~ s/((<)+)/~$1~/g if $text !~ /[~]</;
214             $text =~ s/((>)+)/~$1~/g if $text !~ />[~]/;
215              
216             return '' . "$text";
217             }
218              
219              
220             sub _row_end {
221             my($self, $node, $rules ) = @_;
222              
223             if($self->{'colspan'}) {
224             return $self->{'colspan'};
225             }
226             $self->{'colspan'} = ""
227             }
228              
229              
230             sub _td_start {
231             my($self, $node, $rules ) = @_;
232             my $text = $self->get_elem_contents($node);
233              
234             $text =~ s/\\s*$//m; # for Word tables pasted into editor
235             $text =~ s/\\s*/
/gm;
236             $text =~ s/\\\\/
/gm; # see _p_alignment() comment
237             $text =~ s/</dwfckgTableOpenBRACKET/gm;
238             $self->{'colspan'} = "";
239             my $prefix = $self->SUPER::_td_start($node, $rules);
240              
241             $self->{'in_table'} = 1;
242             $self->{'colspan'} = $node->attr('colspan') || "";
243              
244             my $td_backcolor = "";
245             my %table_header = $self->_get_type($node, ['th', 'th'], 'font');
246              
247             my $align = $node->attr('align') || 'left';
248             $align =~ /^(\w)\w+/;
249             $align = uc($1);
250              
251              
252              
253             if(!%table_header && $prefix !~ /\s*\^\s/) {
254             my $style = $node->attr('style') || '';
255             if($style) {
256             my @styles = split ';', $style;
257             my $td_w; my $td_bg; my $td_a; my $back_color = ""; my $td_width = "";
258             foreach my $s (@styles) {
259             if($s =~ /background-color/) {
260             $td_bg = $s;
261             }
262             elsif($s =~ /width/) {
263             $td_w = $s;
264             }
265             elsif($s =~ /text-align/) {
266             $td_a = $s;
267             }
268             }
269             $back_color = $self->_extract_style_value($td_bg, 'background-color') if $td_bg;
270              
271             $td_width = $self->_extract_style_value($td_w, 'width') if $td_w;
272            
273             if(($align eq 'L') && ($td_a = $self->_extract_style_value($td_a, 'text-align'))) {
274             $td_a =~ /^(\w)\w+/;
275             $align = uc($1);
276             }
277              
278             if($back_color || $td_width || $align) {
279             $td_backcolor = " #$align" . $back_color . $td_width . '# ';
280             }
281             }
282             else {
283             $align = "#$align#";
284             }
285             }
286             else {
287             $align = ""; $td_backcolor = "";
288             }
289              
290              
291              
292             if(%table_header) {
293            
294             if($table_header{'th'} =~ 'th') {
295             $prefix = ' ^ ' ;
296            
297             }
298             else {
299             $prefix = ' | ' ;
300             }
301              
302             $text = $self->trim($text);
303             }
304              
305              
306             my $suffix = $self->_td_end($node,$rules);
307              
308             if($self->{'colspan'}) {
309             $self->{'colspan'} = chop $suffix; # save suffix marker for _row_end
310            
311             }
312            
313             $text =~ s/\n/ /gm;
314              
315             $td_backcolor = $align if !$td_backcolor;
316              
317             return $prefix . $td_backcolor . $text . $suffix;
318            
319             }
320              
321              
322             sub _extract_values {
323             my ($self, $attr, $values) = @_;
324             my $HTML_Elements = scalar @$values;
325              
326             return $values->[0]->{$attr} if exists $values->[0]->{$attr};
327              
328             $HTML_Elements--;
329             if($HTML_Elements) {
330             return $values->[$HTML_Elements]->{$attr} if exists $values->[$HTML_Elements]->{$attr};
331             }
332            
333             return "";
334             }
335              
336             sub _extract_style_value {
337             my($self, $at, $search_term) = @_;
338              
339             my($attribute, $value) = split /:/, $at;
340              
341             $attribute =~ s/^\s+//;
342             $attribute =~ s/\s+$//;
343              
344             $value =~ s/^\s+//;
345             $value =~ s/\s+$//;
346              
347             return $value if $search_term && $attribute eq $search_term;
348             return 0;
349             }
350              
351              
352             sub _get_type {
353             my ($self, $node, $attrs,$type) = @_;
354              
355             my $valuepat = ${$style_patterns{$type}};
356             my %ret_values=();
357              
358              
359             my @values_1 = $node->look_down($attrs->[0], $valuepat);
360             if(@values_1) {
361             my $retv = $self->_extract_values($attrs->[0],\@values_1);
362             if($retv) {
363             $ret_values{$attrs->[0]} = $retv;
364             }
365             }
366            
367            
368             my @values_2 = $node->look_down($attrs->[1], $valuepat) if scalar @$attrs == 2;
369              
370             if(@values_2) {
371             my $retv = $self->_extract_values($attrs->[1],\@values_2);
372             if($retv) {
373             $ret_values{$attrs->[1]} = $retv;
374             }
375             }
376              
377             if(!exists $ret_values{$attrs->[1]} || !exists $ret_values{$attrs->[0]}) {
378             my @style_values = $node->look_down('style',$font_pattern);
379             if(@style_values) {
380             my $retv = $self->_extract_values('style',\@style_values);
381            
382             if(!exists $ret_values{$attrs->[0]}) {
383             my $attr_val =$self->_extract_style_value($retv, $attrs->[0]);
384             if($attr_val) {
385             $ret_values{$attrs->[0]} = $attr_val;
386             }
387             }
388             if($attrs->[1] && !exists $ret_values{$attrs->[1]}) {
389             my $attr_val =$self->_extract_style_value($retv, $attrs->[1]);
390             if($attr_val) {
391             $ret_values{$attrs->[1]} = $attr_val;
392             }
393             }
394            
395             }
396             }
397              
398              
399              
400             if(!exists $ret_values{$attrs->[0]}) {
401             my @values_1a = $node->look_up($attrs->[0], $valuepat);
402             if(@values_1a) {
403             my $retv = $self->_extract_values($attrs->[0],\@values_1a);
404             if($retv) {
405             $ret_values{$attrs->[0]} = $retv;
406             }
407             }
408             }
409            
410             if($attrs->[1] && !exists $ret_values{$attrs->[1]}) {
411             my @values_2a = $node->look_up($attrs->[1], $valuepat);
412             if(@values_2a) {
413             my $retv = $self->_extract_values($attrs->[1],\@values_2a);
414             if($retv) {
415             $ret_values{$attrs->[1]} = $retv;
416             }
417             }
418             }
419            
420             if(!exists $ret_values{$attrs->[0]}) {
421             my @values_3 = $node->attr_get_i($attrs->[0]);
422             foreach my $val(@values_3) {
423             $ret_values{$attrs->[0]} = $val; # if there is a hit, take the first one, there
424             last; # shouldn't be more
425             }
426             }
427            
428             if($attrs->[1] && !exists $ret_values{$attrs->[1]}) {
429             my @values_4 = $node->attr_get_i($attrs->[1]);
430             foreach my $val(@values_4) {
431              
432             $ret_values{$attrs->[1]} = $val;
433             last; # ditto to above
434             }
435             }
436              
437              
438             if(!exists $ret_values{$attrs->[1]} || !exists $ret_values{$attrs->[0]}) {
439             my @values_5 = $node->attr_get_i("style");
440             foreach my $at(@values_5) {
441              
442             if(!exists $ret_values{$attrs->[0]}) {
443             if($at =~ /$attrs->[0]/) {
444             my $attr_val =$self->_extract_style_value($at,$attrs->[0]);
445             if($attr_val) {
446             $ret_values{$attrs->[0]} = $attr_val;
447             last;
448             }
449             }
450             }
451              
452             if($attrs->[1] && !exists $ret_values{$attrs->[1]}) {
453             if($at =~ /$attrs->[1]/) {
454             my $attr_val =$self->_extract_style_value($at, $attrs->[1]);
455             if($attr_val) {
456             $ret_values{$attrs->[1]} = $attr_val;
457             last;
458             }
459             }
460             }
461            
462             }
463             }
464            
465            
466             return %ret_values;
467             }
468              
469              
470              
471              
472             sub _span_contents {
473             my($self, $node, $rules ) = @_;
474              
475             my $text = $self->get_elem_contents($node);
476             my $current_text = ""; # used where more than one span occurs in the markup retrieved as $text
477              
478             if($text =~ /^\s*<(color|font).*?\/(color|font)/) {
479             return $text;
480             }
481              
482             elsif($text =~ /(.*?)<(color|font).*?\/(color|font)/) {
483             $current_text = $1;
484             my $tmp = $current_text;
485             $tmp =~ s/([*\/\-'"{\[\]\(\)])/\\$1/gms; # escape regex pattern characters
486             $text =~ s/^$tmp//;
487             $current_text = $self->trim($current_text);
488             }
489            
490              
491             my %color_atts = $self->_get_type($node, ['color','background-color'], 'color');
492             if(%color_atts) {
493             my $fg = (exists $color_atts{'color'}) ? ($color_atts{'color'}) : "";
494             my $bg = (exists $color_atts{'background-color'}) ? ($color_atts{'background-color'}) : "";
495              
496             $fg = 'black' if($fg eq 'white' && !$bg);
497              
498             if($fg eq $bg && $text =~ /
499             $fg = '_dummy_';
500             }
501             if($fg eq $bg && $text =~ /
502             $fg = '_dummy_';
503             }
504             if($current_text) {
505             $current_text = "$current_text";
506             }
507             $text = "$current_text$text";
508             }
509              
510             elsif($current_text) {
511            
512             }
513              
514             my $pat = qr//;
515             $text =~ s/($pat)\s*$pat(.*?)<\/color>/$1$2/;
516            
517              
518             my %font_atts = $self->_get_type($node, ['size', 'face'], 'font');
519             if(%font_atts) {
520             my $size = (exists $font_atts{'size'}) ? ($font_atts{'size'}) : "_dummy_";
521             my $face = (exists $font_atts{'face'}) ? ($font_atts{'face'}) : "_dummy_";
522             if($current_text) {
523             $text = "$current_text$text";
524             }
525             else {
526             $text = "$text";
527             }
528             }
529              
530             if(!%font_atts && !%color_atts && $current_text) {
531             $text = "$current_text$text";
532             }
533              
534             return $text;
535             }
536              
537             sub clean_text {
538             my($self, $text) = @_;
539             $text =~ s/<.*?>/ /gs;
540             $text =~ s/\s+/ /gs;
541            
542             return $text;
543             }
544              
545             sub _sup {
546             my($self, $node, $rules ) = @_;
547              
548             my $text = $self->get_elem_contents($node) || "";
549             return "" if $text =~ /Anchor/i;
550             $text = $self->trim($text);
551             return "$text";
552              
553             }
554              
555             sub _code_types {
556             my($self, $node, $rules ) = @_;
557             my $text = $self->get_elem_contents($node) || "";
558              
559             $text = $self->trim($text);
560             $text =~ s/[\\]{2}/\n/g; # required for IE which places
at end of each line
561             $text =~ s/\n/$NL_marker\n/gms;
562              
563            
564             $text =~ s/</dwfckgOpenPAREN/gms; # substitution for open angle bracket
565             $text =~ s/\/\*/dwfckgOpen_C_COMMENT/gms;
566             $text =~ s/\*\//dwfckgClosed_C_COMMENT/gms;
567            
568             $text = $self->replace_formats($text);
569              
570             $text =~ s/<.*?>/ /gs; # remove all tags
571              
572             $text =~ s/(?
573              
574             return "" if ! $text;
575             $self->{'code'} = 1;
576             return "$NL_marker\n${NL_marker}\n$text $NL_marker\n\n";
577             }
578              
579              
580             sub _li_start {
581             my($self, $node, $rules ) = @_;
582             my $text = $self->get_elem_contents($node) || "";
583             $text =~ s///gm;
584             $text =~ s/<\/align>//gm;
585             $text =~ s/^\s*//m;
586             $text =~ s/\n{2,}/\n/gm;
587             my $type = $self->SUPER::_li_start($node, $rules);
588             $self->{'list_output'} = 1; # signal postprocess_output to clean up lists
589             return "$NL_marker\n$type" . $text . $EOL;
590             }
591              
592              
593             sub _hanging_formats {
594             my ($self,$str, $search, $format) = @_;
595            
596             my @matches = $str =~ /$_format_regex{$format}/g;
597            
598             my $instances = scalar @matches;
599              
600             return $str if($instances % 2 == 0);
601             if($instances == 1) {
602             $str =~ s/$_format_regex{$format}/$_format_esc{$format}/;
603            
604             return $str;
605             }
606              
607             return $str;
608             }
609              
610             sub replace_formats {
611             my ($self, $output) = @_;
612              
613             my @format_ids = ( 'b', 'i', 'u');
614             foreach my $format(@format_ids) {
615             if($output =~ /$_format_regex{$format}/gms) {
616             $output =~ s/(?_hanging_formats($1,$2, $format)/mse;
617             }
618             }
619              
620             $output =~ s/_<(\w+)_>/$_formats{$1}/g;
621              
622             if($output =~ /$dw_code{'regex'}/) {
623             $output =~ s/$dw_code{'regex'}/$dw_code{'esc'}/gms;
624             }
625              
626             return $output;
627             }
628              
629             sub _p_alignment {
630             my($self, $node, $rules ) = @_;
631            
632             my $output = $self->get_elem_contents($node) || "";
633              
634             $output =~ s/<[\/]*indent>//gm;
635             $output =~ s/^([\s\x{a0}]+)/$1<\/indent>/m;
636             $output =~ s/<\/indent>//;
637             $output =~ s/\\\\/
/gm; # replace \\ with
for p-to-line-break tool
638              
639             $output = $self->replace_formats($output);
640              
641             if($output =~ /^\s*[*\-]{1}\s+/gms) {
642            
643             $self->{'list_output'} = 1;
644             return "\n $output$EOL";
645             }
646             elsif($output =~/\
647             return $output;
648             }
649              
650             if($output =~ /
651              
652             return $output;
653             }
654              
655              
656             if($node->parent) {
657             my $tag = $node->parent->tag();
658             if($tag eq 'td') {
659             return $output . "
"; ## use
, not
so it doesn't getlopped of in postprocess
660             ## converted to
in _td_start and since I don't see
661             ## where it would get lopped off, this could probably be
`
662             }
663             }
664              
665            
666              
667             my $newline = "";
668             if($self->{'do_nudge'} && $output =~ /^\s{3,}/) {
669             $newline = "";
670             }
671              
672              
673             if($output =~ /^(\s*|([\\][\\]))\s*.*?<\/indent>\s*$/) {
674             return "";
675             }
676              
677             if($self->{'strike_out'}) {
678             $self->{'strike_out'} = 0;
679             return $output;
680             }
681              
682             my $align = $node->{'style'} if exists $node->{'style'};
683            
684             my $align_tag = "";
685              
686             my $aligns_cnt = 0;
687             if($align) { # there have been some styles with multiple attributes, hence this code
688             my @styles = split ';', $align if($align);
689             foreach my $style(@styles) {
690             my ($att, $val) = split ':', $style;
691            
692             if ($att && $val)
693             {
694             $att =~ s/^\s//;
695             $att =~ s/\s$//;
696             if($att =~ /(text\-align|margin\-left)/) {
697             $val =~ s/^\s//;
698             $val =~ s/\s$//;
699             $align_tag .= " ";
700             $aligns_cnt++;
701             }
702             }
703             }
704             }
705            
706             if(!$align_tag) {
707             $align_tag = "";
708             }
709            
710            
711              
712             $output = "${align_tag}\n${output}\n";
713            
714             $aligns_cnt--;
715             if($aligns_cnt) {
716             for(1...$aligns_cnt) {
717             $output .= " ";
718             }
719             }
720            
721              
722             $output=~s/http\:(?!\/\/)/http:\/\//gsm;
723             $output =~ s/\/{3,}/\/\//g; # removes extra newline markers at start and end of images
724              
725              
726             if($output =~ s/[\x{a0}\x{b7}]+<\/align>/<\/align>/gms) {
727             return $output;
728             }
729              
730             $output =~ s/[\s\n\x{a0}\x{b7}]+<\/align>//gms;
731             $output =~ s/[\s\n\x{a0}\x{b7}]+<\/align>//gms;
732             $output =~ s/[\s\n\x{a0}\x{b7}]+<\/align>//gms;
733             return "" if(!$output);
734              
735             if($output =~/[\s\n]*<\/align>/gms) {
736             $output = '
';
737             }
738             return $newline . $output;
739             }
740              
741              
742             sub _dwimage_markup {
743             my ($self, $src, $align) = @_;
744             if($src !~ /^http:/) {
745             $src =~ s/\//:/g;
746             if($src !~ /^:/) {
747             $src = ":$src";
748             }
749             }
750              
751             if($align eq 'center') {
752             return "\n\n{{ $src }}\n\n";
753             }
754             if($align eq 'right') {
755             return "\n{{ $src}} ";
756             }
757             if($align eq 'left') {
758             return "\n{{$src }}";
759             }
760             if($align =~ /\d+px/) {
761             return "\n{{$src}}\n";
762             }
763              
764             if($align =~ /bottom|baseline/) {
765             return "\n{{$src}}\n";
766             }
767            
768             return "{{$src}} ";
769             }
770              
771             sub _image {
772             my($self, $node, $rules ) = @_;
773              
774             my $src = $node->attr('src') || '';
775              
776             return "" if(!$src);
777              
778             my $alignment = $self->_image_alignment($node);
779             if(!$alignment && $node->parent) {
780             $alignment = $self->_image_alignment($node->parent);
781             }
782             my $w = $node->attr('width') || 0;
783             my $h = $node->attr('height') || 0;
784              
785             if(!$w) {
786             $w = $node->attr('w') || 0;
787             }
788             if(!$h) {
789             $h = $node->attr('h') || 0;
790             }
791              
792             if( $w and $h ) {
793             $src .= "?${w}x${h}";
794             } elsif( $w ) {
795             $src .= "?${w}";
796             }
797              
798             if($src =~ /editor\/images\/smiley\/msn/) {
799             if($src =~ /media=(http:.*?\.gif)/) {
800             $src = $1;
801              
802             }
803             else {
804             my $HOST = $self->base_uri;
805             $src = 'http://' . $HOST . $src if($src !~ /$HOST/);
806              
807             }
808              
809             return "{{$src}} ";
810             }
811              
812              
813             if($src !~ /userfiles\/image/ && $src !~ /\/data\/media/) {
814             my @elems = split /=/, $src;
815             $src = pop @elems;
816             return $self->_dwimage_markup($src,$alignment) if($src !~ /^http:/);
817             return $self->_dwimage_markup($src,$alignment);
818             }
819              
820              
821             if($src =~ s/^(.*?)\/userfiles\/image\///) {
822             return $self->_dwimage_markup($src,$alignment);
823             }
824              
825             if($src =~ s/^(.*?)\/data\/media\///) {
826             return $self->_dwimage_markup($src,$alignment);
827             }
828              
829             my $img_url = $self->SUPER::_image($node, $rules);
830              
831             $img_url =~ s/%25/%/g;
832             $img_url =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg;
833             $img_url =~ s/%25/%/g;
834             $img_url =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg;
835              
836              
837             my @elems = split /media/, $img_url;
838             if (scalar @elems > 2) {
839             my $last_el = pop @elems;
840             my $dw_markup = $last_el;
841             if($dw_markup =~ s/^(.*?)userfiles\/image\///) {
842             return $self->_dwimage_markup($dw_markup, $alignment);
843             }
844             $img_url = $elems[0] . 'media' . $last_el;
845             }
846              
847              
848             return $img_url;
849            
850             }
851              
852             sub _image_alignment {
853             my ($self, $node) = @_;
854             my $align = $node->attr('align') || "";
855             if($align) {
856             $align = 'center' if $align eq 'middle';
857             return $align;
858             }
859             if($node->parent) {
860             my $p = $node->parent;
861             my %atts = $p->all_external_attr();
862             foreach my $at(keys %atts) {
863              
864             if($at eq 'style') {
865              
866             if($atts{$at} =~ /margin-left:\s+(\d+px)/) {
867             return $1;
868             }
869             elsif($atts{$at} =~ /text-align:\s+(\w+)/) {
870             return $1;
871             }
872             elsif($atts{$at} =~ /bottom|base/) {
873             return 'bottom';
874             }
875              
876             }
877             }
878             }
879              
880             return "";
881             }
882              
883              
884             sub format_InternalLink {
885             my($self, $node, $file) = @_;
886              
887             my $inner_text = $self->get_elem_contents($node) || "";
888             $file = ":$file" if $file !~/^:/;
889             if($inner_text) {
890             return "$file|$inner_text";
891             }
892             return $file;
893             }
894              
895             sub _link {
896             my($self, $node, $rules ) = @_;
897             my $url = $node->attr('href') || '';
898              
899             my $name = $node->attr('name') || '';
900              
901             my $internal_link = "";
902             my $_text = $self->get_elem_contents($node) || "";
903            
904             # these manage shares
905             if($url =~ /file:/) {
906             $url =~ s/^file:[\/]+/__SHARE__/;
907             $url =~ s/\//\\/g;
908             $self->{'share'} = 1;
909             return "[[$url|$_text]]";
910             }
911             elsif($url =~/[\\]{2,}/) {
912             $url =~ s/^[\/]+//;
913             $url =~ s/^[\\]+/__SHARE__/;
914             $_text = $url if(!$_text);
915             $_text =~ s/\\{2,}/\\/g;
916              
917             $self->{'share'} = 1;
918             return "[[$url|$_text]]";
919             }
920              
921             if($name) {
922             return '~~ANCHOR:' . $name . ':::' . $_text .'~~';
923             }
924              
925             if ($url !~ /^\W*http:/ && $url =~ /\/(doku\.php\?id=)?:?((((\w)(\w|_)*)*:)*(\w(\w|_)*)*)$/) {
926              
927             my $format = $self->format_InternalLink($node,$2);
928             $node->attr('href', $format);
929             $internal_link = "[[$format]]";
930             }
931             elsif($url !~ /^\W*http:/ && $url =~ /\/(doku\.php\?id=)?:?(\w+\#[\-\.\w]+)$/) {
932              
933             my $format = $self->format_InternalLink($node,$2);
934             $node->attr('href', $format);
935             $internal_link = "[[$format]]";
936              
937             }
938              
939             elsif($url !~ /^\W*http:/ && $url =~ /\/(doku\.php\?id=)?:(.*?)\.(.*)/ && $3 !~/gif|png|txt|jpg|jpeg/) {
940              
941             my $format = $self->format_InternalLink($node,"$2.$3");
942             $node->attr('href', $format);
943             $internal_link = "{{$format}}";
944             }
945              
946             elsif ($url =~ /^mailto:(.*)(\?.*)?/) {
947             return "<" . $1 . ">";
948             }
949             elsif ($url =~ /\/lib\/exe\/fetch.php\?/) {
950             my $content = $self->get_elem_contents($node);
951             if ($content =~ /\{\{.*\}\}/) {
952             return $content;
953             }
954             if ($url =~ /media=(.*)(&.*)?/) {
955             return "{{" . $1 . "}}" if lc $1 eq lc $content;
956             return "{{" . $1 . "|" . $self->_as_text($node) . "}}";
957             }
958             }
959             elsif ($url =~ /\/lib\/exe\/detail.php\?/) {
960             my $content = $self->get_elem_contents($node);
961             return $content;
962             }
963              
964             my $output= $internal_link? $internal_link : $self->SUPER::_link($node, $rules);
965              
966             my $text = $self->get_elem_contents($node) || "";
967              
968             my $left_alignment = ""; #actually any alignment, not just left
969             if($text =~ s/(\)//) {
970             $left_alignment = $1;
971             $text =~ s/<\/align>//;
972             $output =~ s/\//;
973             $output =~ s/<\/align>//;
974             }
975              
976             elsif($text =~ s/(\)//) {
977             $left_alignment = $1;
978             $text =~ s/<\/align>//;
979             $output =~ s/\//;
980             $output =~ s/<\/align>//;
981              
982             }
983              
984              
985             my $external_open; my $external_closed;
986            
987             if($text =~ /(\<(font|color).*?\>)(.*?)(<\/\2>)/) {
988             $external_open = $1;
989             $external_closed = $4;
990             my $interior = $3;
991             $text = $3;
992              
993             if($interior =~ /(\<(font|color).*?\>)(.*?)(<\/\2>)/) {
994             $text = $3;
995             $external_open = "${external_open}$1";
996             $external_closed = "$4${external_closed}";
997             }
998            
999             }
1000              
1001             if($text =~ /^\s*[\\]{2}s*/) { return ""; }
1002            
1003             my $emphasis = "";
1004            
1005             if($text =~ /(_<\w_>)/) {
1006             $emphasis = "$1";
1007             }
1008             # $emphasis = "" if $emphasis =~ /$_format_regex{'i'}/;
1009            
1010             if($text =~ /^(<.*?\w>).*?(<\/.*?>)$/) {
1011             my $start = $1;
1012             my $end = $2;
1013              
1014             my $start_pat = $start;
1015             my $end_pat = $end;
1016             $start_pat =~ s/(\W)/\\$1/g;
1017             $end_pat =~ s/(\W)/\\$1/g;
1018              
1019             $text =~ /^$start_pat(.*?)$end_pat$/;
1020              
1021             $text = $1;
1022             if($text =~ /\W{2}(.*?)\W{2}/) {
1023             $text= $1;
1024             }
1025             $output =~ s/\|$start_pat.*?$end_pat/|$text/;
1026             $output = "$start${emphasis}${output}${emphasis}$end";
1027             }
1028             elsif($emphasis) {
1029             $output =~ s/${emphasis}//g;
1030             $output = "${emphasis}${output}${emphasis}";
1031             }
1032              
1033             if($left_alignment) {
1034             $output = "$left_alignment${output}";
1035             }
1036              
1037              
1038             if($external_open) {
1039             my($url, $name) = split /\|/, $output;
1040              
1041             $name =~ s/<.*?>//g;
1042              
1043             $output = $url . '|' . $name;
1044             $output = $external_open . $output . $external_closed;
1045             }
1046              
1047             return $output;
1048             }
1049              
1050              
1051             sub _block {
1052             my($self, $node, $rules ) = @_;
1053             my $text = $self->get_elem_contents($node) || "";
1054              
1055             if($text =~ /
1056             return $text;
1057             }
1058              
1059             $self->{'block'} = 1;
1060             my $bg = "";
1061             my $fg = "";
1062             my $width = '80';
1063             my $border = "";
1064             my $font = "";
1065             my $face = "";
1066             my $size = "";
1067             my $margin = "40";
1068              
1069             my $style = $node->attr('style');
1070             my @styles = split(';',$style);
1071             foreach my $at(@styles) {
1072             my $val = "";
1073             if($val = $self->_extract_style_value($at,'width') ) {
1074             $val =~ s/\%//;
1075             $width = int($val);
1076             }
1077             elsif($val = $self->_extract_style_value($at,'background-color') ) {
1078             $bg = $val;
1079             }
1080             elsif($val = $self->_extract_style_value($at,'color') ) {
1081             $fg = $val;
1082             }
1083             elsif($val = $self->_extract_style_value($at,'border') ) {
1084             $border = "$val";
1085             }
1086              
1087             elsif($val = $self->_extract_style_value($at,'border-left') ) {
1088             $border = "$val";
1089             }
1090              
1091             elsif($val = $self->_extract_style_value($at,'margin-left') ) {
1092             if($val =~/(\d+)/) {
1093             $margin = $1;
1094             }
1095             }
1096            
1097             }
1098              
1099             my $basics = $self->_get_basic_attributes ($node);
1100             if(!$fg) {
1101             $fg = $basics->{'color'};
1102             }
1103             $face = $basics->{'face'};
1104             $size = $basics->{'size'};
1105             if($face || $size) {
1106             $font = "$face/$size";
1107             }
1108              
1109             $fg = 'black' if($fg eq 'white' && (!$bg || !$fg || $bg eq 'white'));
1110              
1111             if(!$bg) {
1112             if($text =~ //) {
1113             $bg = $1;
1114             }
1115             }
1116              
1117             my $block = "";
1118             $text =~ s/^\s+//; # trim
1119             $text =~ s/\s+$//;
1120             $text =~ s/\n{2,}/\n/g; # multi
1121              
1122             return $block . $text . '
';
1123              
1124             }
1125             sub _table {
1126             my($self, $node, $rules ) = @_;
1127             my $text = $self->get_elem_contents($node) || "";
1128            
1129             my $table_header = "";
1130             my $align = $node->attr('align');
1131             if($align) {
1132              
1133             if($text=~/$NL_marker(.*?)$NL_marker/gms) {
1134             my $row = $1;
1135             my @cols = $row=~/[\|\^]/g;
1136             my $cols = scalar @cols;
1137             if($cols) {
1138             $cols--;
1139             $table_header = $NL_marker . "|++THEAD++ ALIGN=$align" . '|' x $cols ;
1140             }
1141             }
1142             }
1143            
1144             # return "${table_header}$text
";
1145             return "${table_header}$text++END_TABLE++";
1146            
1147             }
1148              
1149             sub postprocess_output {
1150            
1151             my($self, $outref ) = @_;
1152              
1153             $$outref =~ s/^[\s\xa0]+//; # trim
1154             $$outref =~ s/[\s\xa0]+$//;
1155             $$outref =~ s/\n{2,}/\n/g; # multi
1156             $$outref =~ s/\x{b7}/\x{a0}/gm;
1157            
1158             $$outref =~ s/(?<=<\/align>)\s+(?=)//gms; #### ???? ####
1159             $$outref =~ s/\s+$//gms;
1160             $$outref=~s/http\:(?!\/\/)/http:\/\//gsm; # replace missing forward slashes
1161             $$outref=~s/__(\/\/[\[\{])/$1/gsm; # remove underlining markup
1162             $$outref=~s/([\}\]]\/\/)__/$1/gsm; # ditto
1163            
1164             $$outref =~ s/<\/dwfckg>//gms;
1165              
1166             $$outref =~ s/\^<\/align>//g; # remove aligns at top of file
1167             $$outref =~ s/[\s\n]*[\s\n]*<\/align>[\s\n]*//gsm; # remove empty aligns
1168             $$outref =~ s/\n*<\/indent>//gms;
1169            
1170            
1171             $$outref =~ s/(?)(?<\/align> /gms;
1172             $$outref =~ s/$NL_marker/\n/gms;
1173              
1174             $$outref =~ s/\n\s*(?=\|\n)//gms;
1175              
1176             $$outref =~ s/^\s+//gms; # delete spaces at start of lines
1177             $$outref =~ s/([\n\s]*<\/align>[\n\s]*)+//gms;
1178             $$outref =~ s/([\n\s]*<\/align>[\n\s]*)+//gms;
1179              
1180             $$outref =~ s/\n[\\](2)s*/\n/gms;
1181            
1182             $$outref =~ s/(?<=)[\n\s]+(?=<\/align>)//gms;
1183             $$outref =~ s/\n{3,}/\n/gms;
1184              
1185             $$outref =~ s/[\n\s]+<\/align>[\\]{2}\s*//gms;
1186              
1187             $$outref =~ s/([\s\n]*<\/align>[\s\n]*){2,}/<\/align>/gms;
1188             $$outref =~ s/([\s\n]*<\/align>[\s\n]*){2,}/<\/align>/gms;
1189             $$outref =~ s/([\s\n]*<\/align>[\s\n]*){2,}/<\/align>/gms;
1190             $$outref =~ s/([\s\n]*<\/align>([\s\n])*){2,}/$2 ? "<\/align>$2" : "<\/align>"/gmse;
1191              
1192              
1193             if($self->{'list_output'}) { # start with look behind for bold
1194             $$outref =~ s/(?_format_list($1,$2, $3)/gmse;
1195             }
1196              
1197             $$outref =~ s/(?!\n)/\n/gms;
1198             $$outref =~ s/\s*<\/font>//gms;
1199              
1200             $$outref =~ s/([\/\{]{2})*(\s*)(?_clean_url($3,$1, $2)/egms;
1201              
1202             $$outref =~ s/__SHARE__/\\\\/gms if $self->{'share'};
1203              
1204             $$outref =~s/$EOL//g;
1205              
1206             $$outref =~ s/<\/code>//gms;
1207             $$outref =~ s/[\W]+<\/code>//gms;
1208            
1209             $$outref =~ s/[\n\s]*(.*?<\/align>[\n\s]*)<\/align>/$1/gms;
1210              
1211             $self->del_xtra_c_aligns($$outref);
1212              
1213             $$outref =~ s/<\/align>\s*
/<\/align>/gms;
1214             $$outref =~ s/\n+/\n/gms;
1215              
1216              
1217             if($self->{'in_table'}) { # insure tables start with newline
1218             $$outref =~ s/align>\s*(\||\^)/align>\n$1/gms;
1219             $$outref =~ s/[\\]{2}(?=\s+\|)//gms; # remove line breaks at ends of cells
1220             }
1221              
1222              
1223             $$outref =~ s/(?<=\<\/align>)(\s*[\\]{2}\s*)+//gms;
1224             $$outref =~ s/(?<=\<\/align>)\s*
\s*//gms;
1225              
1226             if($self->{'code'}) {
1227             $$outref =~ s/x\00/ /gms;
1228             $$outref =~ s/(?<=)(.*?)(?=<\/code>)/$self->fix_code($1)/gmse;
1229             $$outref =~ s/<\/code>[\s\n]*$/<\/code>
/;
1230             }
1231              
1232             $$outref =~ s/<\/block>/<\/block><\/align>/gm if $self->{'block'};
1233             $$outref .= "\n" unless $$outref =~ /\n\n$/m;
1234            
1235             if($$outref !~ //gms) {
1236             $$outref = '' . $$outref;
1237             }
1238            
1239            
1240             if($self->{'in_table'}) {
1241             $$outref =~ s/\+\+END_TABLE\+\+[\s\n]*$/
/;
1242             $$outref =~ s/\++END_TABLE\+\+//g;
1243             }
1244              
1245             }
1246              
1247              
1248             sub del_xtra_c_aligns {
1249             my ($self, $text) = @_;
1250              
1251             my @left = $text=~ /(
1252             my @right = $text=~ /(<\/align)/gms;
1253              
1254              
1255             if(scalar @right > scalar @left) {
1256             my $oCount = 0;
1257             my $cCount = 0;
1258              
1259             $text =~ s/(()|(<\/align>))/$self->fix_aligns($1, \$oCount, \$cCount)/egms;
1260             }
1261             }
1262              
1263              
1264             sub fix_aligns {
1265             my ($self, $align, $open, $close) = @_;
1266             $$close++ if $align =~ /<\/align/;
1267             $$open++ if $align =~ //;
1268              
1269             if ($$close > $$open) {
1270             $$close--;
1271             return ""
1272             }
1273              
1274             return $align;
1275             }
1276              
1277             sub _clean_url {
1278             my($self,$url, $markup, $spaces) = @_;
1279             return "{{$url" if $url=~/editor\/images\/smiley\/msn/;
1280              
1281             if($markup =~/\{\{/) { ## an external image, the first pair of brackets have been removed by regex
1282             return $markup . $spaces . $url;
1283             }
1284              
1285             my $italics="";
1286             if($markup =~ /\//) {
1287             $italics='//';
1288             }
1289              
1290             $url =~ s/^[^h]+//ms;
1291             $url =~ s/['"\/*_]{2,}$//ms;
1292              
1293             return $italics .'[[' . $url . ']]' . $italics;
1294             }
1295              
1296             sub _format_list {
1297             my($self,$type, $item, $rest_of_sel) = @_;
1298             my $text = "${type} ${item}${rest_of_sel}";
1299             return $text if($text =~ /->/);
1300             my $prefix = ""; # any matter which precedes list
1301              
1302             my $p = 0;
1303             pos($text) = 0;
1304             while($text =~ /(.*?)(?
1305             $prefix .= $1;
1306             $p = pos($text);
1307             }
1308             pos($text) = $p;
1309             $text =~ /(.*?)$EOL/gms;
1310             $item = $self->trim($1) if $1;
1311             if($item eq '-' || $item eq '*') {
1312             $item = ""; #remove empty list items,they overlap previous line
1313             }
1314             $item =~ s/\s*<\/align>\s*$//gm;
1315             return "$prefix\n $item";
1316              
1317             }
1318              
1319             sub fix_code {
1320             my ($self, $text) = @_;
1321             $text =~ s/($nudge_char)*<\/indent>//gms;
1322             # $text =~ s/$code_NL/\n/gms;
1323             $text =~ s/[\x{b7}\x{a0}]//gms if $self->{'do_nudge'} ;
1324             return $text;
1325             }
1326              
1327              
1328             sub trim {
1329             my($self,$text) = @_;
1330             $text =~ s/^\s+//;
1331             $text =~ s/\s+$//;
1332             return $text;
1333             }
1334              
1335             sub log {
1336             my($self, $where, $data) = @_;
1337             my $fh = $self->{'_fh'};
1338             $where = "" if ! $where;
1339             $data = "" if ! $data;
1340             if( $fh ) {
1341             print $fh "$where: $data\n";
1342             }
1343             }
1344              
1345              
1346             sub DESTROY {
1347             my $self=shift;
1348             my $fh = $self->{_fh};
1349              
1350             if( $fh ) {
1351             print $fh "\n-----------\n\n";
1352             close($fh);
1353             }
1354              
1355             }
1356              
1357              
1358             sub _get_basic_attributes {
1359             my($self, $node) = @_;
1360              
1361             my $fg = '';
1362             my $bg = '';
1363             my %color_atts = $self->_get_type($node, ['color','background-color'], 'color');
1364             if(%color_atts) {
1365             $fg = (exists $color_atts{'color'}) ? ($color_atts{'color'}) : "";
1366             $bg = (exists $color_atts{'background-color'}) ? ($color_atts{'background-color'}) : "";
1367             }
1368              
1369            
1370             my $face = '';
1371             my $size = '';
1372             my %font_atts = $self->_get_type($node, ['size', 'face'], 'font');
1373             if(%font_atts) {
1374             $face = (exists $font_atts{'face'}) ? ($font_atts{'face'}) : '';
1375             $size = (exists $font_atts{'size'}) ? ($font_atts{'size'}) : "";
1376             }
1377             return { 'face'=>$face, 'size'=>$size,'color'=>$fg, 'background'=>$bg };
1378             }
1379              
1380             sub _header {
1381             my($self, $node, $rules ) = @_;
1382              
1383             my $text = $self->_as_text($node);
1384              
1385             $node->tag =~ /(\d)/;
1386              
1387             my $pre_and_post_fix = "=" x (7 - $1);
1388              
1389             my $str = "\n" . "$NL_marker\n$pre_and_post_fix" . $text . "$pre_and_post_fix\n\n";
1390             return $str;
1391             }
1392              
1393             sub _as_text {
1394             my($self, $node) = @_;
1395             my $text = join '', map { $self->__get_text($_) } $node->content_list;
1396             return defined $text ? $text : '';
1397             }
1398              
1399             sub __get_text {
1400             my($self, $node) = @_;
1401             $node->normalize_content();
1402             if( $node->tag eq '~text' ) {
1403             return $node->attr('text');
1404             } elsif( $node->tag eq '~comment' ) {
1405             return '';
1406             } else {
1407             my $output = $self->_as_text($node)||'';
1408             return $output;
1409             }
1410             }
1411              
1412              
1413              
1414             1;
1415              
1416              
1417              
1418