Ignore:
Timestamp:
11/18/09 06:27:14 (3 years ago)
Author:
MichaelTempest
Message:

Item2369: Add rowspan support to the WYSIWYG editor
This also fixed a bug in how Web.WikiWord links are rendered, if the link is the only text in the table cell.

Tables are no longer "normalised" when converting from HTML to TML by appending |'s so that every row has the same number of |'s. The logic to do so when supporting rowspans is complex (read: likely to be buggy) and the "normalisation" feature is seldom used (it only had any effect if the HTML has a table with a hole in it.) Taking out the normalisation does not result in loss of content.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/WysiwygPlugin/lib/Foswiki/Plugins/WysiwygPlugin/TML2HTML.pm

    r5503 r5573  
    115115    if ($content =~ /[$TT0$TT1$TT2]/o) { 
    116116        # There should never be any of these in the text at this point. 
    117         # If there are, then the conversion failed.  
     117        # If there are, then the conversion failed. 
    118118        die("Invalid characters in HTML after conversion") if $options->{dieOnError}; 
    119         # Encode the original TML as verbatim-style HTML,  
     119 
     120        # Encode the original TML as verbatim-style HTML, 
    120121        # so that the user has uncorrupted TML, at least. 
    121122        my $originalContent = $_[1]; 
     
    245246 
    246247# Lifted straight out of DevelopBranch Render.pm 
     248# Then modified to include TablePlugin's approach to table rendering 
    247249sub _getRenderedVersion { 
    248250    my ( $this, $text, $refs ) = @_; 
     
    342344    my $inList      = 0;         # True when within a list type 
    343345    my $inTable     = 0;         # True when within a table type 
     346    my %table       = (); 
    344347    my $inParagraph = 1;         # True when within a P 
    345348    my @result      = ('<p>'); 
     
    354357            $this->_addListItem( \@result, '', '', '' ) if $inList; 
    355358            $inList = 0; 
    356             unless ($inTable) { 
    357                 push( 
    358                     @result, 
    359                     CGI::start_table( 
    360                         { border => 1, cellpadding => 0, cellspacing => 1 } 
    361                     ) 
    362                 ); 
    363             } 
    364             push( @result, _emitTR($1) ); 
     359            push( @result, _processTableRow( $1, $inTable, \%table ) ); 
    365360            $inTable = 1; 
    366361            next; 
     
    368363 
    369364        if ($inTable) { 
    370             push( @result, CGI::end_table() ); 
     365            push( @result, _emitTable( \%table ) ); 
    371366            $inTable = 0; 
    372367        } 
     
    468463 
    469464    if ($inTable) { 
    470         push( @result, '</table>' ); 
     465        push( @result, _emitTable( \%table ) ); 
    471466    } 
    472467    elsif ($inList) { 
     
    529524 
    530525    return $text; 
     526} 
     527 
     528sub _processTableRow { 
     529 
     530    my ( $theRow, $inTable, $state ) = @_; 
     531    my @result; 
     532    my $firstRow = 0; 
     533    if ( !$inTable ) { 
     534 
     535        %$state = ( curTable => [], rowspan => [] ); 
     536        $firstRow = 1; 
     537    } 
     538 
     539    $theRow =~ s/\t/   /go;     # change tabs to space 
     540    $theRow =~ s/\s*$//o;       # remove trailing spaces 
     541    $theRow =~ s/^(\s*)\|//;    # Remove leading junk 
     542    my $pre = $1; 
     543 
     544    $theRow =~ 
     545      s/(\|\|+)/'colspan'.$Foswiki::TranslationToken.length($1)."\|"/geo 
     546      ;                         # calc COLSPAN 
     547    my $colCount = 0; 
     548    my @row      = (); 
     549    my $span     = 0; 
     550    my $value    = ''; 
     551 
     552    my $rowspanEnabled = Foswiki::Func::getContext()->{'TablePluginEnabled'}; 
     553 
     554    foreach ( split( /\|/, $theRow ) ) { 
     555        my $attr = {}; 
     556        $span = 1; 
     557        if (s/colspan$Foswiki::TranslationToken([0-9]+)//) { 
     558            $span = $1; 
     559            $attr->{colspan} = $span; 
     560        } 
     561        s/^\s+$/ &nbsp; /o; 
     562        my ( $left, $right ) = ( 0, 0 ); 
     563        if (/^(\s*)(.*?)(\s*)$/) { 
     564            $left  = length($1); 
     565            $_     = $2; 
     566            $right = length($3); 
     567        } 
     568        if ( $left == 1 && $right < 2 ) { 
     569 
     570            # Treat left=1 and right=0 like 1 and 1 - Item5220 
     571        } 
     572        elsif ( $left > $right ) { 
     573            $attr->{class} = 'align-right'; 
     574            $attr->{style} = 'text-align: right'; 
     575        } 
     576        elsif ( $left < $right ) { 
     577            $attr->{class} = 'align-left'; 
     578            $attr->{style} = 'text-align: left'; 
     579        } 
     580        elsif ( $left > 1 ) { 
     581            $attr->{class} = 'align-center'; 
     582            $attr->{style} = 'text-align: center'; 
     583        } 
     584 
     585        if (    $rowspanEnabled 
     586            and !$firstRow 
     587            and /^(\s|<[^>]*>)*\^(\s|<[^>]*>)*$/ ) 
     588        {    # row span above 
     589            $state->{rowspan}->[$colCount]++; 
     590            push @row, { text => $value, type => 'Y' }; 
     591        } 
     592        else { 
     593            for ( my $col = $colCount ; $col < ( $colCount + $span ) ; $col++ ) 
     594            { 
     595                if ( defined( $state->{rowspan}->[$col] ) 
     596                    && $state->{rowspan}->[$col] ) 
     597                { 
     598                    my $nRows = scalar( @{ $state->{curTable} } ); 
     599                    my $rspan = $state->{rowspan}->[$col] + 1; 
     600                    if ( $rspan > 1 ) { 
     601                        $state->{curTable}->[ $nRows - $rspan ][$col]->{attrs} 
     602                          ->{rowspan} = $rspan; 
     603                    } 
     604                    undef( $state->{rowspan}->[$col] ); 
     605                } 
     606            } 
     607 
     608            my $type = ''; 
     609            if (/^\s*\*(.*)\*\s*$/) { 
     610                $value = $1; 
     611                $type  = 'th'; 
     612            } 
     613            else { 
     614                if (/^\s*(.*?)\s*$/) {    # strip white spaces 
     615                    $_ = $1; 
     616                } 
     617                $value = $_; 
     618                $type  = 'td'; 
     619            } 
     620 
     621            $value = ' ' . $value if $value =~ /^(?:\*|==?|__?)[^\s]/; 
     622            $value = $value . ' ' if $value =~ /[^\s](?:\*|==?|__?)$/; 
     623 
     624            push @row, { text => $value, attrs => $attr, type => $type }; 
     625        } 
     626 
     627        while ( $span > 1 ) { 
     628            push @row, { text => $value, type => 'X' }; 
     629            $colCount++; 
     630            $span--; 
     631        } 
     632        $colCount++; 
     633    } 
     634    push @{ $state->{curTable} }, \@row; 
     635    push @{ $state->{pre} },      $pre; 
     636    return; 
     637} 
     638 
     639sub _emitTable { 
     640    my ($state) = @_; 
     641 
     642    my @result; 
     643    push( @result, 
     644        CGI::start_table( { border => 1, cellpadding => 0, cellspacing => 1 } ) 
     645    ); 
     646 
     647    #Flush out any remaining rowspans 
     648    for ( my $i = 0 ; $i < scalar( @{ $state->{rowspan} } ) ; $i++ ) { 
     649        if ( defined( $state->{rowspan}->[$i] ) && $state->{rowspan}->[$i] ) { 
     650            my $nRows = scalar( @{ $state->{curTable} } ); 
     651            my $rspan = $state->{rowspan}->[$i] + 1; 
     652            my $r     = $nRows - $rspan; 
     653            $state->{curTable}->[$r][$i]->{attrs} ||= {}; 
     654            if ( $rspan > 1 ) { 
     655                $state->{curTable}->[$r][$i]->{attrs}->{rowspan} = $rspan; 
     656            } 
     657        } 
     658    } 
     659 
     660    my $rowCount     = 0; 
     661    my $numberOfRows = scalar( @{ $state->{curTable} } ); 
     662 
     663    my @headerRowList = (); 
     664    my @bodyRowList   = (); 
     665 
     666    my $isPastHeaderRows = 0; 
     667 
     668    foreach my $row ( @{ $state->{curTable} } ) { 
     669        my $rowtext  = ''; 
     670        my $colCount = 0; 
     671 
     672        # keep track of header cells: if all cells are header cells, 
     673        # put the row in the thead section 
     674        my $headerCellCount = 0; 
     675        my $numberOfCols    = scalar(@$row); 
     676 
     677        foreach my $fcell (@$row) { 
     678 
     679            # check if cell exists 
     680            next if ( !$fcell || !$fcell->{type} ); 
     681 
     682            my $tableAnchor = ''; 
     683            next 
     684              if ( $fcell->{type} eq 'X' ) 
     685              ;    # data was there so sort could work with col spanning 
     686            my $type = $fcell->{type}; 
     687            my $cell = $fcell->{text}; 
     688            my $attr = $fcell->{attrs} || {}; 
     689 
     690            if ( $type eq 'th' ) { 
     691                $headerCellCount++; 
     692            } 
     693            else { 
     694                $type = 'td' unless $type eq 'Y'; 
     695            }      ###if( $type eq 'th' ) 
     696 
     697            $colCount++; 
     698            next if ( $type eq 'Y' ); 
     699            my $fn = 'CGI::' . $type; 
     700            no strict 'refs'; 
     701            $rowtext .= &$fn( $attr, " $cell " ); 
     702            use strict 'refs'; 
     703        }    # foreach my $fcell ( @$row ) 
     704 
     705        my $rowHTML = $state->{pre}->[$rowCount] . CGI::Tr($rowtext); 
     706 
     707        my $isHeaderRow = ( $headerCellCount == $colCount ); 
     708        if ( !$isHeaderRow ) { 
     709 
     710        # don't include non-adjacent header rows to the top block of header rows 
     711            $isPastHeaderRows = 1; 
     712        } 
     713 
     714        if ( $isHeaderRow && !$isPastHeaderRows ) { 
     715            push( @headerRowList, $rowHTML ); 
     716        } 
     717        else { 
     718            push @bodyRowList, $rowHTML; 
     719        } 
     720 
     721        $rowCount++; 
     722    }    # foreach my $row ( @curTable ) 
     723 
     724    push @result, @headerRowList, @bodyRowList; 
     725 
     726    push @result, CGI::end_table(); 
     727    return @result; 
    531728} 
    532729 
     
    561758sub _protectVerbatimChars { 
    562759    my $text = shift; 
    563     # $TT0, $TT1 and $TT2 are chr(0), chr(1) and chr(2), respectively.  
     760 
     761    # $TT0, $TT1 and $TT2 are chr(0), chr(1) and chr(2), respectively. 
    564762    # They are handled specially, elsewhere 
    565763    $text =~ s/([\003-\011\013-\037<&>'"])/'&#'.ord($1).';'/ges; 
Note: See TracChangeset for help on using the changeset viewer.