* parse the wiki syntax used to render tables
*
* @private
- *
- * @param $text string
- *
- * @return string
*/
function doTableStuff( $text ) {
wfProfileIn( __METHOD__ );
$lines = StringUtils::explode( "\n", $text );
$out = '';
- $output =& $out;
+ $td_history = array(); # Is currently a td tag open?
+ $last_tag_history = array(); # Save history of last lag activated (td, th or caption)
+ $tr_history = array(); # Is currently a tr tag open?
+ $tr_attributes = array(); # history of tr attributes
+ $has_opened_tr = array(); # Did this table open a <tr> element?
+ $indent_level = 0; # indent level of the table
foreach ( $lines as $outLine ) {
$line = trim( $outLine );
- # empty line, go to next line,
- # but only append \n if outside of table
- if ( $line === '' ) {
- $output .= $outLine;
- if ( !isset( $tables[0] ) ) {
- $output .= "\n";
- }
+ if ( $line === '' ) { # empty line, go to next line
+ $out .= $outLine."\n";
continue;
}
- $firstChars = $line[0];
- if ( strlen( $line ) > 1 ) {
- $firstChars .= in_array( $line[1], array( '}', '+', '-' ) ) ? $line[1] : '';
- }
+
+ $first_character = $line[0];
$matches = array();
- if ( preg_match( '/^(:*)\s*\{\|(.*)$/', $line , $matches ) ) {
- $tables[] = array();
- $table =& $this->last( $tables );
- $table[0] = array(); // first row
- $currentRow =& $table[0];
- $table['indent'] = strlen( $matches[1] );
+ if ( preg_match( '/^(:*)\{\|(.*)$/', $line , $matches ) ) {
+ # First check if we are starting a new table
+ $indent_level = strlen( $matches[1] );
$attributes = $this->mStripState->unstripBoth( $matches[2] );
$attributes = Sanitizer::fixTagAttributes( $attributes , 'table' );
- if ( $attributes !== '' ) {
- $table['attributes'] = $attributes;
- }
- } elseif ( !isset( $tables[0] ) ) {
- // we're outside the table
-
- $out .= $outLine . "\n";
- } elseif ( $firstChars === '|}' ) {
- // trim the |} code from the line
- $line = substr ( $line , 2 );
-
- // Shorthand for last row
- $lastRow =& $this->last( $table );
-
- // a thead at the end becomes a tfoot, unless there is only one row
- // Do this before deleting empty last lines to allow headers at the bottom of tables
- if ( isset( $lastRow['type'] ) && $lastRow['type'] == 'thead' && isset( $table[1] ) ) {
- $lastRow['type'] = 'tfoot';
- for ( $i = 0; isset( $lastRow[$i] ); $i++ ) {
- $lastRow[$i]['type'] = 'th';
- }
- }
+ $outLine = str_repeat( '<dl><dd>' , $indent_level ) . "<table{$attributes}>";
+ array_push( $td_history , false );
+ array_push( $last_tag_history , '' );
+ array_push( $tr_history , false );
+ array_push( $tr_attributes , '' );
+ array_push( $has_opened_tr , false );
+ } elseif ( count( $td_history ) == 0 ) {
+ # Don't do any of the following
+ $out .= $outLine."\n";
+ continue;
+ } elseif ( substr( $line , 0 , 2 ) === '|}' ) {
+ # We are ending a table
+ $line = '</table>' . substr( $line , 2 );
+ $last_tag = array_pop( $last_tag_history );
- // Delete empty last lines
- if ( empty( $lastRow ) ) {
- $lastRow = NULL;
+ if ( !array_pop( $has_opened_tr ) ) {
+ $line = "<tr><td></td></tr>{$line}";
}
- $o = '';
- $curtable = array_pop( $tables );
- #Add a line-ending before the table, but only if there isn't one already
- if ( substr( $out, -1 ) !== "\n" ) {
- $o .= "\n";
+ if ( array_pop( $tr_history ) ) {
+ $line = "</tr>{$line}";
}
- $o .= $this->generateTableHTML( $curtable ) . $line . "\n";
- if ( count( $tables ) > 0 ) {
- $table =& $this->last( $tables );
- $currentRow =& $this->last( $table );
- $currentElement =& $this->last( $currentRow );
-
- $output =& $currentElement['content'];
- } else {
- $output =& $out;
+ if ( array_pop( $td_history ) ) {
+ $line = "</{$last_tag}>{$line}";
}
+ array_pop( $tr_attributes );
+ $outLine = $line . str_repeat( '</dd></dl>' , $indent_level );
+ } elseif ( substr( $line , 0 , 2 ) === '|-' ) {
+ # Now we have a table row
+ $line = preg_replace( '#^\|-+#', '', $line );
- $output .= $o;
-
- } elseif ( $firstChars === '|-' ) {
- // start a new row element
- // but only when we haven't started one already
- if ( count( $currentRow ) != 0 ) {
- $table[] = array();
- $currentRow =& $this->last( $table );
- }
- // Get the attributes, there's nothing else useful in $line now
- $line = substr ( $line , 2 );
+ # Whats after the tag is now only attributes
$attributes = $this->mStripState->unstripBoth( $line );
$attributes = Sanitizer::fixTagAttributes( $attributes, 'tr' );
- if ( $attributes !== '' ) {
- $currentRow['attributes'] = $attributes;
- }
+ array_pop( $tr_attributes );
+ array_push( $tr_attributes, $attributes );
- } elseif ( $firstChars === '|+' ) {
- // a table caption, but only proceed if there isn't one already
- if ( !isset ( $table['caption'] ) ) {
- $line = substr ( $line , 2 );
-
- $c = $this->getCellAttr( $line , 'caption' );
- $table['caption'] = array();
- $table['caption']['content'] = $c[0];
- if ( isset( $c[1] ) ) $table['caption']['attributes'] = $c[1];
- unset( $c );
- $output =& $table['caption']['content'];
- }
- } elseif ( $firstChars === '|' || $firstChars === '!' || $firstChars === '!+' ) {
- // Which kind of cells are we dealing with
- $currentTag = 'td';
- $line = substr ( $line , 1 );
-
- if ( $firstChars === '!' || $firstChars === '!+' ) {
- $line = str_replace ( '!!' , '||' , $line );
- $currentTag = 'th';
- }
+ $line = '';
+ $last_tag = array_pop( $last_tag_history );
+ array_pop( $has_opened_tr );
+ array_push( $has_opened_tr , true );
- // Split up multiple cells on the same line.
- $cells = StringUtils::explodeMarkup( '||' , $line );
- $line = ''; // save memory
+ if ( array_pop( $tr_history ) ) {
+ $line = '</tr>';
+ }
- // decide whether thead to tbody
- if ( !array_key_exists( 'type', $currentRow ) ) {
- $currentRow['type'] = ( $firstChars === '!' ) ? 'thead' : 'tbody' ;
- } elseif ( $firstChars === '|' ) {
- $currentRow['type'] = 'tbody';
+ if ( array_pop( $td_history ) ) {
+ $line = "</{$last_tag}>{$line}";
}
- // Loop through each table cell
- foreach ( $cells as $cell ) {
- // a new cell
- $currentRow[] = array();
- $currentElement =& $this->last( $currentRow );
+ $outLine = $line;
+ array_push( $tr_history , false );
+ array_push( $td_history , false );
+ array_push( $last_tag_history , '' );
+ } elseif ( $first_character === '|' || $first_character === '!' || substr( $line , 0 , 2 ) === '|+' ) {
+ # This might be cell elements, td, th or captions
+ if ( substr( $line , 0 , 2 ) === '|+' ) {
+ $first_character = '+';
+ $line = substr( $line , 1 );
+ }
- $currentElement['type'] = $currentTag;
+ $line = substr( $line , 1 );
- $c = $this->getCellAttr( $cell , $currentTag );
- $currentElement['content'] = $c[0];
- if ( isset( $c[1] ) ) $currentElement['attributes'] = $c[1];
- unset( $c );
+ if ( $first_character === '!' ) {
+ $line = str_replace( '!!' , '||' , $line );
}
- $output =& $currentElement['content'];
- } else {
- $output .= "\n$outLine";
- }
- }
+ # Split up multiple cells on the same line.
+ # FIXME : This can result in improper nesting of tags processed
+ # by earlier parser steps, but should avoid splitting up eg
+ # attribute values containing literal "||".
+ $cells = StringUtils::explodeMarkup( '||' , $line );
- # Remove trailing line-ending (b/c)
- if ( substr( $out, -1 ) === "\n" ) {
- $out = substr( $out, 0, -1 );
- }
+ $outLine = '';
- # Close any unclosed tables
- if ( isset( $tables ) && count( $tables ) > 0 ) {
- for ( $i = 0; $i < count( $tables ); $i++ ) {
- $curtable = array_pop( $tables );
- $curtable = $this->generateTableHTML( $curtable );
- #Add a line-ending before the table, but only if there isn't one already
- if ( substr( $out, -1 ) !== "\n" && $curtable !== "" ) {
- $out .= "\n";
- }
- $out .= $curtable;
- }
- }
+ # Loop through each table cell
+ foreach ( $cells as $cell ) {
+ $previous = '';
+ if ( $first_character !== '+' ) {
+ $tr_after = array_pop( $tr_attributes );
+ if ( !array_pop( $tr_history ) ) {
+ $previous = "<tr{$tr_after}>\n";
+ }
+ array_push( $tr_history , true );
+ array_push( $tr_attributes , '' );
+ array_pop( $has_opened_tr );
+ array_push( $has_opened_tr , true );
+ }
- wfProfileOut( __METHOD__ );
+ $last_tag = array_pop( $last_tag_history );
- return $out;
- }
+ if ( array_pop( $td_history ) ) {
+ $previous = "</{$last_tag}>\n{$previous}";
+ }
- /**
- * Helper function for doTableStuff() separating the contents of cells from
- * attributes. Particularly useful as there's a possible bug and this action
- * is repeated twice.
- *
- * @private
- * @param $cell
- * @param $tagName
- * @return array
- */
- function getCellAttr ( $cell, $tagName ) {
- $attributes = null;
+ if ( $first_character === '|' ) {
+ $last_tag = 'td';
+ } elseif ( $first_character === '!' ) {
+ $last_tag = 'th';
+ } elseif ( $first_character === '+' ) {
+ $last_tag = 'caption';
+ } else {
+ $last_tag = '';
+ }
- $cell = trim ( $cell );
+ array_push( $last_tag_history , $last_tag );
- // A cell could contain both parameters and data
- $cellData = explode ( '|' , $cell , 2 );
+ # A cell could contain both parameters and data
+ $cell_data = explode( '|' , $cell , 2 );
- // Bug 553: Note that a '|' inside an invalid link should not
- // be mistaken as delimiting cell parameters
- if ( strpos( $cellData[0], '[[' ) !== false ) {
- $content = trim ( $cell );
- }
- elseif ( count ( $cellData ) == 1 ) {
- $content = trim ( $cellData[0] );
- } else {
- $attributes = $this->mStripState->unstripBoth( $cellData[0] );
- $attributes = Sanitizer::fixTagAttributes( $attributes , $tagName );
+ # Bug 553: Note that a '|' inside an invalid link should not
+ # be mistaken as delimiting cell parameters
+ if ( strpos( $cell_data[0], '[[' ) !== false ) {
+ $cell = "{$previous}<{$last_tag}>{$cell}";
+ } elseif ( count( $cell_data ) == 1 ) {
+ $cell = "{$previous}<{$last_tag}>{$cell_data[0]}";
+ } else {
+ $attributes = $this->mStripState->unstripBoth( $cell_data[0] );
+ $attributes = Sanitizer::fixTagAttributes( $attributes , $last_tag );
+ $cell = "{$previous}<{$last_tag}{$attributes}>{$cell_data[1]}";
+ }
- $content = trim ( $cellData[1] );
+ $outLine .= $cell;
+ array_push( $td_history , true );
+ }
+ }
+ $out .= $outLine . "\n";
}
- return array( $content, $attributes );
- }
-
- /**
- * Helper function for doTableStuff(). This converts the structured array into html.
- *
- * @private
- *
- * @param $table array
- *
- * @return string
- */
- function generateTableHTML( &$table ) {
- $return = str_repeat( '<dl><dd>' , $table['indent'] );
- $return .= '<table';
- $return .= isset( $table['attributes'] ) ? $table['attributes'] : '';
- $return .= '>';
- unset( $table['attributes'] );
-
- if ( isset( $table['caption'] ) ) {
- $return .= "\n<caption";
- $return .= isset( $table['caption']['attributes'] ) ? $table['caption']['attributes'] : '';
- $return .= '>';
- $return .= $table['caption']['content'];
- $return .= "\n</caption>";
- }
- $lastSection = '';
- $empty = true;
- $simple = true;
-
- // If we only have tbodies, mark table as simple
- for ( $i = 0; isset( $table[$i] ); $i++ ) {
- if ( !count( $table[$i] ) ) continue;
- if ( !isset( $table[$i]['type'] ) ) {
- $table[$i]['type'] = 'tbody';
+ # Closing open td, tr && table
+ while ( count( $td_history ) > 0 ) {
+ if ( array_pop( $td_history ) ) {
+ $out .= "</td>\n";
}
- if ( !$lastSection ) {
- $lastSection = $table[$i]['type'];
- } elseif ( $lastSection != $table[$i]['type'] ) {
- $simple = false;
+ if ( array_pop( $tr_history ) ) {
+ $out .= "</tr>\n";
}
- }
- $lastSection = '';
- for ( $i = 0; isset( $table[$i] ); $i++ ) {
- if ( !count( $table[$i] ) ) continue;
- $empty = false; // check for empty tables
-
- if ( $table[$i]['type'] != $lastSection && !$simple ) {
- $return .= "\n<" . $table[$i]['type'] . '>';
+ if ( !array_pop( $has_opened_tr ) ) {
+ $out .= "<tr><td></td></tr>\n" ;
}
- $return .= "\n<tr";
- $return .= isset( $table[$i]['attributes'] ) ? $table[$i]['attributes'] : '';
- $return .= '>';
- for ( $j = 0; isset( $table[$i][$j] ); $j++ ) {
- if ( !isset( $table[$i][$j]['type'] ) ) $table[$i][$j]['type'] = 'td';
- $return .= "\n<" . $table[$i][$j]['type'];
- $return .= isset( $table[$i][$j]['attributes'] ) ? $table[$i][$j]['attributes'] : '';
- $return .= '>';
-
- $return .= $table[$i][$j]['content'];
- if ( $table[$i][$j]['content'] != '' )
- $return .= "\n";
-
- $return .= '</' . $table[$i][$j]['type'] . '>';
- unset( $table[$i][$j] );
- }
- $return .= "\n</tr>";
+ $out .= "</table>\n";
+ }
- if ( ( !isset( $table[$i + 1] ) && !$simple ) || ( isset( $table[$i + 1] ) && isset( $table[$i + 1]['type'] ) && $table[$i]['type'] != $table[$i + 1]['type'] ) ) {
- $return .= '</' . $table[$i]['type'] . '>';
- }
- $lastSection = $table[$i]['type'];
- unset( $table[$i] );
+ # Remove trailing line-ending (b/c)
+ if ( substr( $out, -1 ) === "\n" ) {
+ $out = substr( $out, 0, -1 );
}
- if ( $empty ) {
- if ( isset( $table['caption'] ) ) {
- $return .= "\n<tr><td></td></tr>";
- } else {
- return '';
- }
+
+ # special case: don't return empty table
+ if ( $out === "<table>\n<tr><td></td></tr>\n</table>" ) {
+ $out = '';
}
- $return .= "\n</table>";
- $return .= str_repeat( '</dd></dl>' , $table['indent'] );
- return $return;
- }
+ wfProfileOut( __METHOD__ );
- /**
- * like end() but only works on the numeric array index and php's internal pointers
- * returns a reference to the last element of an array much like "\$arr[-1]" in perl
- * ignores associative elements and will create a 0 key will a NULL value if there were
- * no numric elements and an array itself if not previously defined.
- *
- * @private
- *
- * @param $arr array
- */
- function &last ( &$arr ) {
- for ( $i = count( $arr ); ( !isset( $arr[$i] ) && $i > 0 ); $i-- ) { }
- return $arr[$i];
+ return $out;
}
/**
|}
!! result
<table>
-<caption>caption
-</caption>
-<tr><td></td></tr>
-</table>
+<caption> caption
+</caption><tr><td></td></tr></table>
!! end
!! result
<table>
<tr>
-<td>1
-</td>
-<td>2
-</td>
-</tr>
-<tr>
-<td>3
-</td>
-<td>4
-</td>
-</tr>
-</table>
-
-!! end
-
-!! test
-Table inside unclosed table w/o cells
-!! input
-{|
-{|
-| foo bar
-|}
-
-!! result
-<table>
-<tr>
-<td>
-<table>
-<tr>
-<td>foo bar
-</td>
-</tr>
-</table>
-</td>
-</tr>
-</table>
-
-!! end
-
-!! test
-Table with thead
-!! input
-{|
-! Number !! Another number
-|-
-| 1 || 2
-|-
-| 3 || 4
-|}
-!! result
-<table>
-<thead>
-<tr>
-<th>Number
-</th>
-<th>Another number
-</th>
-</tr></thead>
-<tbody>
-<tr>
-<td>1
-</td>
-<td>2
-</td>
-</tr>
-<tr>
-<td>3
-</td>
-<td>4
-</td>
-</tr></tbody>
-</table>
-
-!! end
-
-!! test
-Table with multiple captions: Only keep first
-!! input
-{|
-|+ caption 1
-|+ caption 2
-|}
-!! result
-<table>
-<caption>caption 1
-</caption>
-<tr><td></td></tr>
-</table>
-
-!! end
-
-!! test
-Table with multiline caption
-!! input
-{|
-|+ caption 1
-further caption
-|}
-!! result
-<table>
-<caption>caption 1
-further caption
-</caption>
-<tr><td></td></tr>
-</table>
-
-!! end
-!! test
-Table with multiple thead
-!! input
-{|
-! Number !! Another number
-|-
-| 1 || 2
-|-
-! Some other number !! Another number
-|-
-| 3 || 4
-|}
-!! result
-<table>
-<thead>
-<tr>
-<th>Number
-</th>
-<th>Another number
-</th>
-</tr></thead>
-<tbody>
-<tr>
-<td>1
-</td>
-<td>2
-</td>
-</tr></tbody>
-<thead>
-<tr>
-<th>Some other number
-</th>
-<th>Another number
-</th>
-</tr></thead>
-<tbody>
-<tr>
-<td>3
-</td>
-<td>4
-</td>
-</tr></tbody>
-</table>
-
-!! end
-!! test
-Table with thead & tfoot
-!! input
-{|
-! Number !! Another number
-|-
-| 1 || 2
-|-
-! Some other number !! Another number
-|-
-| 3 || 4
-|-
-! Total: 4 !! Total: 6
-|}
-!! result
-<table>
-<thead>
-<tr>
-<th>Number
-</th>
-<th>Another number
-</th>
-</tr></thead>
-<tbody>
-<tr>
-<td>1
-</td>
-<td>2
-</td>
-</tr></tbody>
-<thead>
-<tr>
-<th>Some other number
-</th>
-<th>Another number
-</th>
-</tr></thead>
-<tbody>
-<tr>
-<td>3
-</td>
-<td>4
-</td>
-</tr></tbody>
-<tfoot>
-<tr>
-<th>Total: 4
-</th>
-<th>Total: 6
-</th>
-</tr></tfoot>
-</table>
-
-!! end
-
-!! test
-Table have th inside tfoot
-!! input
-{|
-| cell1 || cell2
-|-
-! Footer1 !! Footer2
-|}
-!! result
-<table>
-<tbody>
-<tr>
-<td>cell1
-</td>
-<td>cell2
-</td>
-</tr></tbody>
-<tfoot>
-<tr>
-<th>Footer1
-</th>
-<th>Footer2
-</th>
-</tr></tfoot>
-</table>
-
-!! end
-
-!! test
-Table have th inside thead
-!! input
-{|
-! Header1 !! Header2
-|-
-| cell1 || cell2
-|}
-!! result
-<table>
-<thead>
-<tr>
-<th>Header1
-</th>
-<th>Header2
-</th>
-</tr></thead>
-<tbody>
+<td> 1 </td>
+<td> 2
+</td></tr>
<tr>
-<td>cell1
-</td>
-<td>cell2
-</td>
-</tr></tbody>
-</table>
-
-!! end
-
-!! test
-Table with list inside
-!! input
-{|
-|style="width: 5em; text-align: center"| gives
-|style="border: 1px dashed #2F6FAB; padding: 0.5em; margin: 0.5em"|
-# Some
-# list
-# Lorem
-# ipsum
-# dolor
-|}
-!! result
-<table>
-<tr>
-<td style="width: 5em; text-align: center">gives
-</td>
-<td style="border: 1px dashed #2F6FAB; padding: 0.5em; margin: 0.5em">
-<ol><li> Some
-</li><li> list
-</li><li> Lorem
-</li><li> ipsum
-</li><li> dolor
-</li></ol>
-</td>
-</tr>
-</table>
-
-!! end
-!! test
-Indented table wrapped in html tags (Related to Bug 26362)
-!! input
-<div>
-:{|
-|-
-| test
-|}</div>
-
-!! result
-<div>
-<dl><dd><table>
-<tr>
-<td>test
-</td>
-</tr>
-</table></dd></dl></div>
-
-!! end
-
-!! test
-Table with multiline contents
-!! input
-{|
-| Alice
-Bob
-dfdfg
-dfg
-|}
-!! result
-<table>
-<tr>
-<td>Alice
-<p>Bob
-dfdfg
-dfg
-</p>
-</td>
-</tr>
-</table>
+<td> 3 </td>
+<td> 4
+</td></tr></table>
!! end
<table border="1" cellpadding="2">
<caption>Multiplication table
</caption>
-<thead>
<tr>
-<th>×
-</th>
-<th>1
-</th>
-<th>2
-</th>
-<th>3
-</th>
-</tr></thead>
-<tbody>
+<th> × </th>
+<th> 1 </th>
+<th> 2 </th>
+<th> 3
+</th></tr>
<tr>
-<th>1
+<th> 1
</th>
-<td>1
-</td>
-<td>2
-</td>
-<td>3
-</td>
-</tr>
+<td> 1 </td>
+<td> 2 </td>
+<td> 3
+</td></tr>
<tr>
-<th>2
+<th> 2
</th>
-<td>2
-</td>
-<td>4
-</td>
-<td>6
-</td>
-</tr>
+<td> 2 </td>
+<td> 4 </td>
+<td> 6
+</td></tr>
<tr>
-<th>3
+<th> 3
</th>
-<td>3
-</td>
-<td>6
-</td>
-<td>9
-</td>
-</tr>
+<td> 3 </td>
+<td> 6 </td>
+<td> 9
+</td></tr>
<tr>
-<th>4
+<th> 4
</th>
-<td>4
-</td>
-<td>8
-</td>
-<td>12
-</td>
-</tr>
+<td> 4 </td>
+<td> 8 </td>
+<td> 12
+</td></tr>
<tr>
-<th>5
+<th> 5
</th>
-<td>5
-</td>
-<td>10
-</td>
-<td>15
-</td>
-</tr></tbody>
-</table>
+<td> 5 </td>
+<td> 10 </td>
+<td> 15
+</td></tr></table>
!! end
!! result
<table align="right" border="1">
<tr>
-<td>Cell 1, row 1
+<td> Cell 1, row 1
</td>
-<td rowspan="2">Cell 2, row 1 (and 2)
+<td rowspan="2"> Cell 2, row 1 (and 2)
</td>
-<td>Cell 3, row 1
-</td>
-</tr>
+<td> Cell 3, row 1
+</td></tr>
<tr>
-<td>Cell 1, row 2
-</td>
-<td>Cell 3, row 2
+<td> Cell 1, row 2
</td>
-</tr>
-</table>
+<td> Cell 3, row 2
+</td></tr></table>
!! end
!! result
<table border="1">
<tr>
-<td>α
+<td> α
</td>
<td>
<table bgcolor="#ABCDEF" border="2">
<tr>
<td>nested
-</td>
-</tr>
+</td></tr>
<tr>
<td>table
-</td>
-</tr>
-</table>
+</td></tr></table>
</td>
<td>the original table again
-</td>
-</tr>
-</table>
+</td></tr></table>
!! end
<table>
<tr>
<td>broken
-</td>
-</tr>
-</table>
-
-!! end
-
-!! test
-Heading inside table (affected by r85922)
-!! input
-{|
-|- valign="top"
-|
-=== Heading ===
-|}
-!! result
-<table>
-<tr valign="top">
-<td>
-<h3><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=1" title="Edit section: Heading">edit</a>]</span> <span class="mw-headline" id="Heading"> Heading </span></h3>
-</td>
-</tr>
-</table>
-
-!! end
-
-!! test
-A table with a caption with unclosed italic
-!! input
-{|
-|+ ''caption
-| Cell
-|}
-!! result
-<table>
-<caption><i>caption</i>
-</caption>
-<tr>
-<td>Cell
-</td>
-</tr>
-</table>
-
-!! end
-
-!! test
-A table with unclosed italic in a cell
-!! input
-{|
-| ''Cell
-|}
-!! result
-<table>
-<tr>
-<td><i>Cell</i>
-</td>
-</tr>
-</table>
+</td></tr></table>
!! end
-!! test
-A table with unclosed italic in a th
-!! input
-{|
-|-
-! ''Cell
-|| Value
-|}
-!! result
-<table>
-<tr>
-<th><i>Cell</i>
-</th>
-<td>Value
-</td>
-</tr>
-</table>
-
-!! end
!! test
Table security: embedded pipes (http://lists.wikimedia.org/mailman/htdig/wikitech-l/2006-April/022293.html)
!! result
<table>
<tr>
-<td>[<a rel="nofollow" class="external free" href="ftp://%7Cx">ftp://%7Cx</a>
-</td>
+<td>[<a rel="nofollow" class="external free" href="ftp://%7Cx">ftp://%7Cx</a></td>
<td>]" onmouseover="alert(document.cookie)">test
</td>
</tr>
!! end
-!! test
-Indented Tables, bug 20078
-!! input
-: {|
-| 1 || 2
-|-
-| 3 || 4
-|}
-!! result
-<dl><dd><table>
-<tr>
-<td>1
-</td>
-<td>2
-</td>
-</tr>
-<tr>
-<td>3
-</td>
-<td>4
-</td>
-</tr>
-</table></dd></dl>
-
-!! end
-
-!! test
-Arbitrary whitespace should not be prepended
-!! input
-{|
-| 1 || 2
-
-|-
-
-
-| 3 || 4
-|-
-
-| 6 || 8
-|}
-!! result
-<table>
-<tr>
-<td>1
-</td>
-<td>2
-</td>
-</tr>
-<tr>
-<td>3
-</td>
-<td>4
-</td>
-</tr>
-<tr>
-<td>6
-</td>
-<td>8
-</td>
-</tr>
-</table>
-
-!! end
-
###
### Internal links
<table>
<tr>
<td>[[{{{1}}}|{{{2}}}]]
-</td>
-</tr>
-</table>
+</td></tr></table>
!! end
</p>
<table>
<tr>
-<td>1
-</td>
-<td>2
-</td>
-</tr>
+<td> 1 </td>
+<td> 2
+</td></tr>
<tr>
-<td>3
-</td>
-<td>4
-</td>
-</tr>
-</table>
+<td> 3 </td>
+<td> 4
+</td></tr></table>
!! end
</p>
<table>
<tr>
-<td>1
-</td>
-<td>2
-</td>
-</tr>
+<td> 1 </td>
+<td> 2
+</td></tr>
<tr>
-<td>3
-</td>
-<td>4
-</td>
-</tr>
-</table>
+<td> 3 </td>
+<td> 4
+</td></tr></table>
!! end
!! result
<table>
<tr>
-<th class="awesome">status
-</th>
-</tr>
-</table>
+<th class="awesome"> status
+</th></tr></table>
!!end
!! result
<table>
<tr>
-<th style="color:blue">status
-</th>
-</tr>
-</table>
+<th style="color:blue"> status
+</th></tr></table>
!!end
!! result
<table>
<tr>
-<th style="/* insecure input */">status
-</th>
-</tr>
-</table>
+<th style="/* insecure input */"> status
+</th></tr></table>
!! end
!! result
<table>
<tr>
-<td></td>
+<td>
+</td>
</tr>
</table>
!! input
==a==
{| STYLE=__TOC__
-|foo
!! result
<h2><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=1" title="Edit section: a">edit</a>]</span> <span class="mw-headline" id="a">a</span></h2>
<table style="__TOC__">
-<tr>
-<td>foo
-</td>
-</tr>
+<tr><td></td></tr>
</table>
!! end
!! result
<table>
<tr>
-<th>https://
-</th>
-<th></th>
+<th>https://</th>
<th></th>
<th></th>
+<th>
+</td>
</tr>
</table>
!! result
<table>
<tr>
-<th><a rel="nofollow" class="external free" href="irc://{{ftp://a">irc://{{ftp://a</a>" onmouseover="alert('hello world');"
+<th> <a rel="nofollow" class="external free" href="irc://{{ftp://a">irc://{{ftp://a</a>" onmouseover="alert('hello world');"
</th>
-<td></td>
+<td>
+</td>
</tr>
</table>
http://===r:::https://b
{|
-
!!result
<p><a rel="nofollow" class="external free" href="http://===r:::https://b">http://===r:::https://b</a>
</p>
-!! end
+<table>
+<tr><td></td></tr>
+</table>
-# Known to produce bad XML for now
+!! end
-# Note: the current result listed for this is not what the original one was,
-# but the original bug was JavaScript injection, which is fixed in any case.
-# It's not clear that the original result listed was any more correct than the
-# current one. Original result:
-# <table>
-# {{{|
-# <u class="|">}}}} >
-# <br style="onmouseover='alert(document.cookie);'" />
-#
-# MOVE YOUR MOUSE CURSOR OVER THIS TEXT
-# <tr>
-# <td></u>
-# </td>
-# </tr>
-# </table>
# Known to produce bad XML for now
!! test
Fuzz testing: Parser24
MOVE YOUR MOUSE CURSOR OVER THIS TEXT
|
!! result
-<p>{{{|
+<table>
+{{{|
<u class="|">}}}} >
<br style="onmouseover='alert(document.cookie);'" />
+
MOVE YOUR MOUSE CURSOR OVER THIS TEXT
-</p>
-<table>
<tr>
<td></u>
</td>
</p>
<table>
<tr>
-<td>1
-</td>
-<td>2
-</td>
-</tr>
+<td> 1 </td>
+<td> 2
+</td></tr>
<tr>
-<td>3
-</td>
-<td>4
-</td>
-</tr>
-</table>
+<td> 3 </td>
+<td> 4
+</td></tr></table>
<p>y
</p>
!! end