Index: trunk/phase3/skins/common/wikibits.js |
— | — | @@ -551,14 +551,7 @@ |
552 | 552 | }; |
553 | 553 | |
554 | 554 | window.ts_makeSortable = function( table ) { |
555 | | - var firstRow; |
556 | | - if ( table.rows && table.rows.length > 0 ) { |
557 | | - if ( table.tHead && table.tHead.rows.length > 0 ) { |
558 | | - firstRow = table.tHead.rows[table.tHead.rows.length-1]; |
559 | | - } else { |
560 | | - firstRow = table.rows[0]; |
561 | | - } |
562 | | - } |
| 555 | + var firstRow = table.rows[0]; |
563 | 556 | if ( !firstRow ) { |
564 | 557 | return; |
565 | 558 | } |
Index: trunk/phase3/tests/parser/parserTests.txt |
— | — | @@ -1237,8 +1237,9 @@ |
1238 | 1238 | |} |
1239 | 1239 | !! result |
1240 | 1240 | <table> |
1241 | | -<caption> caption |
1242 | | -</caption><tr><td></td></tr></table> |
| 1241 | +<caption>caption</caption> |
| 1242 | +<tr><td></td></tr> |
| 1243 | +</table> |
1243 | 1244 | |
1244 | 1245 | !! end |
1245 | 1246 | |
— | — | @@ -1253,13 +1254,14 @@ |
1254 | 1255 | !! result |
1255 | 1256 | <table> |
1256 | 1257 | <tr> |
1257 | | -<td> 1 </td> |
1258 | | -<td> 2 |
1259 | | -</td></tr> |
| 1258 | +<td>1</td> |
| 1259 | +<td>2</td> |
| 1260 | +</tr> |
1260 | 1261 | <tr> |
1261 | | -<td> 3 </td> |
1262 | | -<td> 4 |
1263 | | -</td></tr></table> |
| 1262 | +<td>3</td> |
| 1263 | +<td>4</td> |
| 1264 | +</tr> |
| 1265 | +</table> |
1264 | 1266 | |
1265 | 1267 | !! end |
1266 | 1268 | |
— | — | @@ -1288,49 +1290,46 @@ |
1289 | 1291 | |} |
1290 | 1292 | !! result |
1291 | 1293 | <table border="1" cellpadding="2"> |
1292 | | -<caption>Multiplication table |
1293 | | -</caption> |
| 1294 | +<caption>Multiplication table</caption> |
| 1295 | +<thead> |
1294 | 1296 | <tr> |
1295 | | -<th> × </th> |
1296 | | -<th> 1 </th> |
1297 | | -<th> 2 </th> |
1298 | | -<th> 3 |
1299 | | -</th></tr> |
| 1297 | +<th>×</th> |
| 1298 | +<th>1</th> |
| 1299 | +<th>2</th> |
| 1300 | +<th>3</th> |
| 1301 | +</tr></thead> |
| 1302 | +<tbody> |
1300 | 1303 | <tr> |
1301 | | -<th> 1 |
1302 | | -</th> |
1303 | | -<td> 1 </td> |
1304 | | -<td> 2 </td> |
1305 | | -<td> 3 |
1306 | | -</td></tr> |
| 1304 | +<th>1</th> |
| 1305 | +<td>1</td> |
| 1306 | +<td>2</td> |
| 1307 | +<td>3</td> |
| 1308 | +</tr> |
1307 | 1309 | <tr> |
1308 | | -<th> 2 |
1309 | | -</th> |
1310 | | -<td> 2 </td> |
1311 | | -<td> 4 </td> |
1312 | | -<td> 6 |
1313 | | -</td></tr> |
| 1310 | +<th>2</th> |
| 1311 | +<td>2</td> |
| 1312 | +<td>4</td> |
| 1313 | +<td>6</td> |
| 1314 | +</tr> |
1314 | 1315 | <tr> |
1315 | | -<th> 3 |
1316 | | -</th> |
1317 | | -<td> 3 </td> |
1318 | | -<td> 6 </td> |
1319 | | -<td> 9 |
1320 | | -</td></tr> |
| 1316 | +<th>3</th> |
| 1317 | +<td>3</td> |
| 1318 | +<td>6</td> |
| 1319 | +<td>9</td> |
| 1320 | +</tr> |
1321 | 1321 | <tr> |
1322 | | -<th> 4 |
1323 | | -</th> |
1324 | | -<td> 4 </td> |
1325 | | -<td> 8 </td> |
1326 | | -<td> 12 |
1327 | | -</td></tr> |
| 1322 | +<th>4</th> |
| 1323 | +<td>4</td> |
| 1324 | +<td>8</td> |
| 1325 | +<td>12</td> |
| 1326 | +</tr> |
1328 | 1327 | <tr> |
1329 | | -<th> 5 |
1330 | | -</th> |
1331 | | -<td> 5 </td> |
1332 | | -<td> 10 </td> |
1333 | | -<td> 15 |
1334 | | -</td></tr></table> |
| 1328 | +<th>5</th> |
| 1329 | +<td>5</td> |
| 1330 | +<td>10</td> |
| 1331 | +<td>15</td> |
| 1332 | +</tr></tbody> |
| 1333 | +</table> |
1335 | 1334 | |
1336 | 1335 | !! end |
1337 | 1336 | |
— | — | @@ -1348,17 +1347,15 @@ |
1349 | 1348 | !! result |
1350 | 1349 | <table align="right" border="1"> |
1351 | 1350 | <tr> |
1352 | | -<td> Cell 1, row 1 |
1353 | | -</td> |
1354 | | -<td rowspan="2"> Cell 2, row 1 (and 2) |
1355 | | -</td> |
1356 | | -<td> Cell 3, row 1 |
1357 | | -</td></tr> |
| 1351 | +<td>Cell 1, row 1</td> |
| 1352 | +<td rowspan="2">Cell 2, row 1 (and 2)</td> |
| 1353 | +<td>Cell 3, row 1</td> |
| 1354 | +</tr> |
1358 | 1355 | <tr> |
1359 | | -<td> Cell 1, row 2 |
1360 | | -</td> |
1361 | | -<td> Cell 3, row 2 |
1362 | | -</td></tr></table> |
| 1356 | +<td>Cell 1, row 2</td> |
| 1357 | +<td>Cell 3, row 2</td> |
| 1358 | +</tr> |
| 1359 | +</table> |
1363 | 1360 | |
1364 | 1361 | !! end |
1365 | 1362 | |
— | — | @@ -1378,19 +1375,19 @@ |
1379 | 1376 | !! result |
1380 | 1377 | <table border="1"> |
1381 | 1378 | <tr> |
1382 | | -<td> α |
1383 | | -</td> |
| 1379 | +<td>α</td> |
1384 | 1380 | <td> |
1385 | 1381 | <table bgcolor="#ABCDEF" border="2"> |
1386 | 1382 | <tr> |
1387 | | -<td>nested |
1388 | | -</td></tr> |
| 1383 | +<td>nested</td> |
| 1384 | +</tr> |
1389 | 1385 | <tr> |
1390 | | -<td>table |
1391 | | -</td></tr></table> |
1392 | | -</td> |
1393 | | -<td>the original table again |
1394 | | -</td></tr></table> |
| 1386 | +<td>table</td> |
| 1387 | +</tr> |
| 1388 | +</table></td> |
| 1389 | +<td>the original table again</td> |
| 1390 | +</tr> |
| 1391 | +</table> |
1395 | 1392 | |
1396 | 1393 | !! end |
1397 | 1394 | |
— | — | @@ -1403,8 +1400,9 @@ |
1404 | 1401 | !! result |
1405 | 1402 | <table> |
1406 | 1403 | <tr> |
1407 | | -<td>broken |
1408 | | -</td></tr></table> |
| 1404 | +<td>broken</td> |
| 1405 | +</tr> |
| 1406 | +</table> |
1409 | 1407 | |
1410 | 1408 | !! end |
1411 | 1409 | |
— | — | @@ -1418,8 +1416,7 @@ |
1419 | 1417 | <table> |
1420 | 1418 | <tr> |
1421 | 1419 | <td>[<a rel="nofollow" class="external free" href="ftp://%7Cx">ftp://%7Cx</a></td> |
1422 | | -<td>]" onmouseover="alert(document.cookie)">test |
1423 | | -</td> |
| 1420 | +<td>]" onmouseover="alert(document.cookie)">test</td> |
1424 | 1421 | </tr> |
1425 | 1422 | </table> |
1426 | 1423 | |
— | — | @@ -2685,8 +2682,9 @@ |
2686 | 2683 | !! result |
2687 | 2684 | <table> |
2688 | 2685 | <tr> |
2689 | | -<td>[[{{{1}}}|{{{2}}}]] |
2690 | | -</td></tr></table> |
| 2686 | +<td>[[{{{1}}}|{{{2}}}]]</td> |
| 2687 | +</tr> |
| 2688 | +</table> |
2691 | 2689 | |
2692 | 2690 | !! end |
2693 | 2691 | |
— | — | @@ -2795,13 +2793,14 @@ |
2796 | 2794 | </p> |
2797 | 2795 | <table> |
2798 | 2796 | <tr> |
2799 | | -<td> 1 </td> |
2800 | | -<td> 2 |
2801 | | -</td></tr> |
| 2797 | +<td>1</td> |
| 2798 | +<td>2</td> |
| 2799 | +</tr> |
2802 | 2800 | <tr> |
2803 | | -<td> 3 </td> |
2804 | | -<td> 4 |
2805 | | -</td></tr></table> |
| 2801 | +<td>3</td> |
| 2802 | +<td>4</td> |
| 2803 | +</tr> |
| 2804 | +</table> |
2806 | 2805 | |
2807 | 2806 | !! end |
2808 | 2807 | |
— | — | @@ -2815,13 +2814,14 @@ |
2816 | 2815 | </p> |
2817 | 2816 | <table> |
2818 | 2817 | <tr> |
2819 | | -<td> 1 </td> |
2820 | | -<td> 2 |
2821 | | -</td></tr> |
| 2818 | +<td>1</td> |
| 2819 | +<td>2</td> |
| 2820 | +</tr> |
2822 | 2821 | <tr> |
2823 | | -<td> 3 </td> |
2824 | | -<td> 4 |
2825 | | -</td></tr></table> |
| 2822 | +<td>3</td> |
| 2823 | +<td>4</td> |
| 2824 | +</tr> |
| 2825 | +</table> |
2826 | 2826 | |
2827 | 2827 | !! end |
2828 | 2828 | |
— | — | @@ -4370,8 +4370,9 @@ |
4371 | 4371 | !! result |
4372 | 4372 | <table> |
4373 | 4373 | <tr> |
4374 | | -<th class="awesome"> status |
4375 | | -</th></tr></table> |
| 4374 | +<th class="awesome">status</th> |
| 4375 | +</tr> |
| 4376 | +</table> |
4376 | 4377 | |
4377 | 4378 | !!end |
4378 | 4379 | |
— | — | @@ -4815,8 +4816,9 @@ |
4816 | 4817 | !! result |
4817 | 4818 | <table> |
4818 | 4819 | <tr> |
4819 | | -<th style="color:blue"> status |
4820 | | -</th></tr></table> |
| 4820 | +<th style="color:blue">status</th> |
| 4821 | +</tr> |
| 4822 | +</table> |
4821 | 4823 | |
4822 | 4824 | !!end |
4823 | 4825 | |
— | — | @@ -4829,8 +4831,9 @@ |
4830 | 4832 | !! result |
4831 | 4833 | <table> |
4832 | 4834 | <tr> |
4833 | | -<th style="/* insecure input */"> status |
4834 | | -</th></tr></table> |
| 4835 | +<th style="/* insecure input */">status</th> |
| 4836 | +</tr> |
| 4837 | +</table> |
4835 | 4838 | |
4836 | 4839 | !! end |
4837 | 4840 | |
— | — | @@ -5452,8 +5455,7 @@ |
5453 | 5456 | !! result |
5454 | 5457 | <table> |
5455 | 5458 | <tr> |
5456 | | -<td> |
5457 | | -</td> |
| 5459 | +<td></td> |
5458 | 5460 | </tr> |
5459 | 5461 | </table> |
5460 | 5462 | |
— | — | @@ -5501,8 +5503,7 @@ |
5502 | 5504 | <th>https://</th> |
5503 | 5505 | <th></th> |
5504 | 5506 | <th></th> |
5505 | | -<th> |
5506 | | -</td> |
| 5507 | +<th></th> |
5507 | 5508 | </tr> |
5508 | 5509 | </table> |
5509 | 5510 | |
— | — | @@ -5517,10 +5518,8 @@ |
5518 | 5519 | !! result |
5519 | 5520 | <table> |
5520 | 5521 | <tr> |
5521 | | -<th> <a rel="nofollow" class="external free" href="irc://{{ftp://a">irc://{{ftp://a</a>" onmouseover="alert('hello world');" |
5522 | | -</th> |
5523 | | -<td> |
5524 | | -</td> |
| 5522 | +<th><a rel="nofollow" class="external free" href="irc://{{ftp://a">irc://{{ftp://a</a>" onmouseover="alert('hello world');"</th> |
| 5523 | +<td></td> |
5525 | 5524 | </tr> |
5526 | 5525 | </table> |
5527 | 5526 | |
— | — | @@ -5542,6 +5541,22 @@ |
5543 | 5542 | !! end |
5544 | 5543 | |
5545 | 5544 | # Known to produce bad XML for now |
| 5545 | + |
| 5546 | +# Note: the current result listed for this is not what the original one was, |
| 5547 | +# but the original bug was JavaScript injection, which is fixed in any case. |
| 5548 | +# It's not clear that the original result listed was any more correct than the |
| 5549 | +# current one. Original result: |
| 5550 | +# <table> |
| 5551 | +# {{{| |
| 5552 | +# <u class="|">}}}} > |
| 5553 | +# <br style="onmouseover='alert(document.cookie);'" /> |
| 5554 | +# |
| 5555 | +# MOVE YOUR MOUSE CURSOR OVER THIS TEXT |
| 5556 | +# <tr> |
| 5557 | +# <td></u> |
| 5558 | +# </td> |
| 5559 | +# </tr> |
| 5560 | +# </table> |
5546 | 5561 | !! test |
5547 | 5562 | Fuzz testing: Parser24 |
5548 | 5563 | !! options |
— | — | @@ -5556,15 +5571,14 @@ |
5557 | 5572 | MOVE YOUR MOUSE CURSOR OVER THIS TEXT |
5558 | 5573 | | |
5559 | 5574 | !! result |
5560 | | -<table> |
5561 | | -{{{| |
| 5575 | +<p>{{{| |
5562 | 5576 | <u class="|">}}}} > |
5563 | 5577 | <br style="onmouseover='alert(document.cookie);'" /> |
5564 | | - |
5565 | | -MOVE YOUR MOUSE CURSOR OVER THIS TEXT |
| 5578 | +</p><p>MOVE YOUR MOUSE CURSOR OVER THIS TEXT |
| 5579 | +</p> |
| 5580 | +<table> |
5566 | 5581 | <tr> |
5567 | | -<td></u> |
5568 | | -</td> |
| 5582 | +<td></u></td> |
5569 | 5583 | </tr> |
5570 | 5584 | </table> |
5571 | 5585 | |
— | — | @@ -7735,13 +7749,13 @@ |
7736 | 7750 | </p> |
7737 | 7751 | <table> |
7738 | 7752 | <tr> |
7739 | | -<td> 1 </td> |
7740 | | -<td> 2 |
7741 | | -</td></tr> |
| 7753 | +<td>1</td> |
| 7754 | +<td>2</td> |
| 7755 | +</tr> |
7742 | 7756 | <tr> |
7743 | | -<td> 3 </td> |
7744 | | -<td> 4 |
7745 | | -</td></tr></table> |
| 7757 | +<td>3</td> |
| 7758 | +<td>4</td> |
| 7759 | +</tr></table> |
7746 | 7760 | <p>y |
7747 | 7761 | </p> |
7748 | 7762 | !! end |
Index: trunk/phase3/includes/parser/Parser.php |
— | — | @@ -824,189 +824,283 @@ |
825 | 825 | |
826 | 826 | $lines = StringUtils::explode( "\n", $text ); |
827 | 827 | $out = ''; |
828 | | - $td_history = array(); # Is currently a td tag open? |
829 | | - $last_tag_history = array(); # Save history of last lag activated (td, th or caption) |
830 | | - $tr_history = array(); # Is currently a tr tag open? |
831 | | - $tr_attributes = array(); # history of tr attributes |
832 | | - $has_opened_tr = array(); # Did this table open a <tr> element? |
833 | | - $indent_level = 0; # indent level of the table |
| 828 | + $output =& $out; |
834 | 829 | |
835 | 830 | foreach ( $lines as $outLine ) { |
836 | 831 | $line = trim( $outLine ); |
837 | 832 | |
838 | | - if ( $line === '' ) { # empty line, go to next line |
| 833 | + if ( $line == '') { //empty line, go to next line |
839 | 834 | $out .= $outLine."\n"; |
840 | 835 | continue; |
841 | 836 | } |
842 | | - |
843 | | - $first_character = $line[0]; |
| 837 | + $first_chars = $line[0]; |
| 838 | + if ( strlen($line) > 1) { |
| 839 | + $first_chars .= in_array($line[1], array('}', '+', '-')) ? $line[1] : ''; |
| 840 | + } |
844 | 841 | $matches = array(); |
845 | 842 | |
846 | 843 | if ( preg_match( '/^(:*)\{\|(.*)$/', $line , $matches ) ) { |
847 | | - # First check if we are starting a new table |
848 | | - $indent_level = strlen( $matches[1] ); |
| 844 | + $tables[] = array(); |
| 845 | + $table =& $this->last($tables); |
| 846 | + $table[0] = array(); //first row |
| 847 | + $current_row =& $table[0]; |
849 | 848 | |
| 849 | + $table['indent'] = strlen( $matches[1] ); |
| 850 | + |
850 | 851 | $attributes = $this->mStripState->unstripBoth( $matches[2] ); |
851 | 852 | $attributes = Sanitizer::fixTagAttributes( $attributes , 'table' ); |
852 | 853 | |
853 | | - $outLine = str_repeat( '<dl><dd>' , $indent_level ) . "<table{$attributes}>"; |
854 | | - array_push( $td_history , false ); |
855 | | - array_push( $last_tag_history , '' ); |
856 | | - array_push( $tr_history , false ); |
857 | | - array_push( $tr_attributes , '' ); |
858 | | - array_push( $has_opened_tr , false ); |
859 | | - } elseif ( count( $td_history ) == 0 ) { |
860 | | - # Don't do any of the following |
| 854 | + if ( $attributes !== '' ) { |
| 855 | + $table['attributes'] = $attributes; |
| 856 | + } |
| 857 | + } else if ( !isset($tables[0]) ) { |
| 858 | + // we're outside the table |
| 859 | + |
861 | 860 | $out .= $outLine."\n"; |
862 | | - continue; |
863 | | - } elseif ( substr( $line , 0 , 2 ) === '|}' ) { |
864 | | - # We are ending a table |
865 | | - $line = '</table>' . substr( $line , 2 ); |
866 | | - $last_tag = array_pop( $last_tag_history ); |
| 861 | + } else if ( $first_chars === '|}' ) { |
| 862 | + // trim the |} code from the line |
| 863 | + $line = substr ( $line , 2 ); |
867 | 864 | |
868 | | - if ( !array_pop( $has_opened_tr ) ) { |
869 | | - $line = "<tr><td></td></tr>{$line}"; |
| 865 | + // Shorthand for last row |
| 866 | + $last_row =& $this->last($table); |
| 867 | + |
| 868 | + // a thead at the end becomes a tfoot, unless there is only one row |
| 869 | + // Do this before deleting empty last lines to allow headers at the bottom of tables |
| 870 | + if ( isset($last_row['type'] ) && $last_row['type'] == 'thead' && isset($table[1])) { |
| 871 | + $last_row['type'] = 'tfoot'; |
| 872 | + for($i = 0; isset($last_row[$i]); $i++ ) { |
| 873 | + $last_row[$i]['type'] = 'td'; |
| 874 | + } |
870 | 875 | } |
871 | 876 | |
872 | | - if ( array_pop( $tr_history ) ) { |
873 | | - $line = "</tr>{$line}"; |
| 877 | + // Delete empty last lines |
| 878 | + if ( empty($last_row) ) { |
| 879 | + $last_row = NULL; |
874 | 880 | } |
| 881 | + $o = $this->printTableHtml( array_pop($tables) ) . $line; |
875 | 882 | |
876 | | - if ( array_pop( $td_history ) ) { |
877 | | - $line = "</{$last_tag}>{$line}"; |
| 883 | + if ( count($tables) > 0 ) { |
| 884 | + $table =& $this->last($tables); |
| 885 | + $current_row =& $this->last($table); |
| 886 | + $current_element =& $this->last($current_row); |
| 887 | + |
| 888 | + $output =& $current_element['content']; |
| 889 | + } else { |
| 890 | + $output =& $out; |
878 | 891 | } |
879 | | - array_pop( $tr_attributes ); |
880 | | - $outLine = $line . str_repeat( '</dd></dl>' , $indent_level ); |
881 | | - } elseif ( substr( $line , 0 , 2 ) === '|-' ) { |
882 | | - # Now we have a table row |
883 | | - $line = preg_replace( '#^\|-+#', '', $line ); |
884 | 892 | |
885 | | - # Whats after the tag is now only attributes |
| 893 | + $output .= $o; |
| 894 | + |
| 895 | + } else if ( $first_chars === '|-' ) { |
| 896 | + // start a new row element |
| 897 | + // but only when we haven't started one already |
| 898 | + if( count($current_row) != 0 ) { |
| 899 | + $table[] = array(); |
| 900 | + $current_row =& $this->last($table); |
| 901 | + } |
| 902 | + // Get the attributes, there's nothing else useful in $line now |
| 903 | + $line = substr ( $line , 2 ); |
886 | 904 | $attributes = $this->mStripState->unstripBoth( $line ); |
887 | 905 | $attributes = Sanitizer::fixTagAttributes( $attributes, 'tr' ); |
888 | | - array_pop( $tr_attributes ); |
889 | | - array_push( $tr_attributes, $attributes ); |
| 906 | + if( $attributes !== '') { |
| 907 | + $current_row['attributes'] = $attributes; |
| 908 | + } |
| 909 | + |
| 910 | + } else if ( $first_chars === '|+' ) { |
| 911 | + // a table caption |
| 912 | + $line = substr ( $line , 2 ); |
| 913 | + |
| 914 | + $c = $this->getCellAttr($line , 'caption'); |
| 915 | + $table['caption'] = array(); |
| 916 | + $table['caption']['content'] = $c[0]; |
| 917 | + if(isset($c[1])) $table['caption']['attributes'] = $c[1]; |
| 918 | + unset($c); |
890 | 919 | |
891 | | - $line = ''; |
892 | | - $last_tag = array_pop( $last_tag_history ); |
893 | | - array_pop( $has_opened_tr ); |
894 | | - array_push( $has_opened_tr , true ); |
| 920 | + $output =& $table['caption']; |
| 921 | + } else if ( $first_chars === '|' || $first_chars === '!' || $first_chars === '!+' ) { |
| 922 | + // Which kind of cells are we dealing with |
| 923 | + $this_tag = 'td'; |
| 924 | + $line = substr ( $line , 1 ); |
895 | 925 | |
896 | | - if ( array_pop( $tr_history ) ) { |
897 | | - $line = '</tr>'; |
| 926 | + if ( $first_chars === '!' || $first_chars === '!+' ) { |
| 927 | + $line = str_replace ( '!!' , '||' , $line ); |
| 928 | + $this_tag = 'th'; |
898 | 929 | } |
899 | 930 | |
900 | | - if ( array_pop( $td_history ) ) { |
901 | | - $line = "</{$last_tag}>{$line}"; |
902 | | - } |
| 931 | + // Split up multiple cells on the same line. |
| 932 | + $cells = StringUtils::explodeMarkup( '||' , $line ); |
| 933 | + $line = ''; // save memory |
903 | 934 | |
904 | | - $outLine = $line; |
905 | | - array_push( $tr_history , false ); |
906 | | - array_push( $td_history , false ); |
907 | | - array_push( $last_tag_history , '' ); |
908 | | - } elseif ( $first_character === '|' || $first_character === '!' || substr( $line , 0 , 2 ) === '|+' ) { |
909 | | - # This might be cell elements, td, th or captions |
910 | | - if ( substr( $line , 0 , 2 ) === '|+' ) { |
911 | | - $first_character = '+'; |
912 | | - $line = substr( $line , 1 ); |
| 935 | + // decide whether thead to tbody |
| 936 | + if ( !array_key_exists('type', $current_row) ) { |
| 937 | + $current_row['type'] = ( $first_chars === '!' ) ? 'thead' : 'tbody' ; |
| 938 | + } else if( $first_chars === '|' ) { |
| 939 | + $current_row['type'] = 'tbody'; |
913 | 940 | } |
914 | 941 | |
915 | | - $line = substr( $line , 1 ); |
| 942 | + // Loop through each table cell |
| 943 | + foreach ( $cells as $cell ) { |
| 944 | + // a new cell |
| 945 | + $current_row[] = array(); |
| 946 | + $current_element =& $this->last($current_row); |
916 | 947 | |
917 | | - if ( $first_character === '!' ) { |
918 | | - $line = str_replace( '!!' , '||' , $line ); |
| 948 | + $current_element['type'] = $this_tag; |
| 949 | + |
| 950 | + $c = $this->getCellAttr($cell , $this_tag); |
| 951 | + $current_element['content'] = $c[0]; |
| 952 | + if(isset($c[1])) $current_element['attributes'] = $c[1]; |
| 953 | + unset($c); |
919 | 954 | } |
| 955 | + $output =& $current_element['content']; |
| 956 | + |
| 957 | + } else { |
| 958 | + $output .= $outLine."\n"; |
| 959 | + } |
| 960 | + } |
| 961 | + |
| 962 | + # Remove trailing line-ending (b/c) |
| 963 | + if ( substr( $out, -1 ) === "\n" ) { |
| 964 | + $out = substr( $out, 0, -1 ); |
| 965 | + } |
| 966 | + |
| 967 | + #Close any unclosed tables |
| 968 | + if (isset($tables) && count($tables) > 0 ) { |
| 969 | + for ($i = 0; $i < count($tables); $i++) { |
| 970 | + $out .= $this->printTableHtml( array_pop($tables) ); |
| 971 | + } |
| 972 | + } |
| 973 | + |
| 974 | + wfProfileOut( __METHOD__ ); |
920 | 975 | |
921 | | - # Split up multiple cells on the same line. |
922 | | - # FIXME : This can result in improper nesting of tags processed |
923 | | - # by earlier parser steps, but should avoid splitting up eg |
924 | | - # attribute values containing literal "||". |
925 | | - $cells = StringUtils::explodeMarkup( '||' , $line ); |
| 976 | + return $out; |
| 977 | + } |
926 | 978 | |
927 | | - $outLine = ''; |
928 | 979 | |
929 | | - # Loop through each table cell |
930 | | - foreach ( $cells as $cell ) { |
931 | | - $previous = ''; |
932 | | - if ( $first_character !== '+' ) { |
933 | | - $tr_after = array_pop( $tr_attributes ); |
934 | | - if ( !array_pop( $tr_history ) ) { |
935 | | - $previous = "<tr{$tr_after}>\n"; |
936 | | - } |
937 | | - array_push( $tr_history , true ); |
938 | | - array_push( $tr_attributes , '' ); |
939 | | - array_pop( $has_opened_tr ); |
940 | | - array_push( $has_opened_tr , true ); |
941 | | - } |
| 980 | + /** |
| 981 | + * Helper function for doTableStuff() separating the contents of cells from |
| 982 | + * attributes. Particularly useful as there's a possible bug and this action |
| 983 | + * is repeated twice. |
| 984 | + * |
| 985 | + * @private |
| 986 | + */ |
| 987 | + function getCellAttr ($cell , $tag_name) { |
| 988 | + $content = null; |
| 989 | + $attributes = null; |
942 | 990 | |
943 | | - $last_tag = array_pop( $last_tag_history ); |
| 991 | + $cell = trim ( $cell ); |
944 | 992 | |
945 | | - if ( array_pop( $td_history ) ) { |
946 | | - $previous = "</{$last_tag}>\n{$previous}"; |
947 | | - } |
| 993 | + // A cell could contain both parameters and data |
| 994 | + $cell_data = explode ( '|' , $cell , 2 ); |
948 | 995 | |
949 | | - if ( $first_character === '|' ) { |
950 | | - $last_tag = 'td'; |
951 | | - } elseif ( $first_character === '!' ) { |
952 | | - $last_tag = 'th'; |
953 | | - } elseif ( $first_character === '+' ) { |
954 | | - $last_tag = 'caption'; |
955 | | - } else { |
956 | | - $last_tag = ''; |
957 | | - } |
| 996 | + // Bug 553: Note that a '|' inside an invalid link should not |
| 997 | + // be mistaken as delimiting cell parameters |
| 998 | + if ( strpos( $cell_data[0], '[[' ) !== false ) { |
| 999 | + $content = trim ( $cell ); |
| 1000 | + } |
| 1001 | + else if ( count ( $cell_data ) == 1 ) { |
| 1002 | + $content = trim ( $cell_data[0] ); |
| 1003 | + } |
| 1004 | + else { |
| 1005 | + $attributes = $this->mStripState->unstripBoth( $cell_data[0] ); |
| 1006 | + $attributes = Sanitizer::fixTagAttributes( $attributes , $tag_name ); |
958 | 1007 | |
959 | | - array_push( $last_tag_history , $last_tag ); |
| 1008 | + $content = trim ( $cell_data[1] ); |
| 1009 | + } |
| 1010 | + return array($content, $attributes); |
| 1011 | + } |
960 | 1012 | |
961 | | - # A cell could contain both parameters and data |
962 | | - $cell_data = explode( '|' , $cell , 2 ); |
963 | 1013 | |
964 | | - # Bug 553: Note that a '|' inside an invalid link should not |
965 | | - # be mistaken as delimiting cell parameters |
966 | | - if ( strpos( $cell_data[0], '[[' ) !== false ) { |
967 | | - $cell = "{$previous}<{$last_tag}>{$cell}"; |
968 | | - } elseif ( count( $cell_data ) == 1 ) { |
969 | | - $cell = "{$previous}<{$last_tag}>{$cell_data[0]}"; |
970 | | - } else { |
971 | | - $attributes = $this->mStripState->unstripBoth( $cell_data[0] ); |
972 | | - $attributes = Sanitizer::fixTagAttributes( $attributes , $last_tag ); |
973 | | - $cell = "{$previous}<{$last_tag}{$attributes}>{$cell_data[1]}"; |
974 | | - } |
| 1014 | + /** |
| 1015 | + * Helper function for doTableStuff(). This converts the structured array into html. |
| 1016 | + * |
| 1017 | + * @private |
| 1018 | + */ |
| 1019 | + function printTableHtml (&$t) { |
| 1020 | + $r = "\n"; |
| 1021 | + $r .= str_repeat( '<dl><dd>' , $t['indent'] ); |
| 1022 | + $r .= '<table'; |
| 1023 | + $r .= isset($t['attributes']) ? $t['attributes'] : ''; |
| 1024 | + $r .= '>'; |
| 1025 | + unset($t['attributes']); |
975 | 1026 | |
976 | | - $outLine .= $cell; |
977 | | - array_push( $td_history , true ); |
978 | | - } |
979 | | - } |
980 | | - $out .= $outLine . "\n"; |
| 1027 | + if ( isset($t['caption']) ) { |
| 1028 | + $r .= "\n<caption"; |
| 1029 | + $r .= isset($t['caption']['attributes']) ? $t['caption']['attributes'] : ''; |
| 1030 | + $r .= '>'; |
| 1031 | + $r .= $t['caption']['content']; |
| 1032 | + $r .= '</caption>'; |
981 | 1033 | } |
982 | | - |
983 | | - # Closing open td, tr && table |
984 | | - while ( count( $td_history ) > 0 ) { |
985 | | - if ( array_pop( $td_history ) ) { |
986 | | - $out .= "</td>\n"; |
| 1034 | + $last_section = ''; |
| 1035 | + $empty = true; |
| 1036 | + $simple = true; |
| 1037 | + |
| 1038 | + //If we only have tbodies, mark table as simple |
| 1039 | + for($i = 0; isset($t[$i]); $i++ ) { |
| 1040 | + if ( !count( $t[$i]) ) continue; |
| 1041 | + if ( !$last_section ) { |
| 1042 | + $last_section = $t[$i]['type']; |
| 1043 | + } else if ($last_section != $t[$i]['type']) { |
| 1044 | + $simple = false; |
| 1045 | + break; |
| 1046 | + } |
| 1047 | + } |
| 1048 | + $last_section = ''; |
| 1049 | + for($i = 0; isset($t[$i]); $i++ ) { |
| 1050 | + // Check for empty tables |
| 1051 | + if ( count( $t[$i]) ) { |
| 1052 | + $empty = false; |
| 1053 | + } else { |
| 1054 | + continue; |
987 | 1055 | } |
988 | | - if ( array_pop( $tr_history ) ) { |
989 | | - $out .= "</tr>\n"; |
| 1056 | + if( $t[$i]['type'] != $last_section && !$simple ) { |
| 1057 | + $r .= "\n<" . $t[$i]['type'] . '>'; |
990 | 1058 | } |
991 | | - if ( !array_pop( $has_opened_tr ) ) { |
992 | | - $out .= "<tr><td></td></tr>\n" ; |
| 1059 | + |
| 1060 | + $r .= "\n<tr"; |
| 1061 | + $r .= isset($t[$i]['attributes']) ? $t[$i]['attributes'] : ''; |
| 1062 | + $r .= '>'; |
| 1063 | + for($j = 0; isset($t[$i][$j]); $j++ ) { |
| 1064 | + $r .= "\n<" . $t[$i][$j]['type']; |
| 1065 | + $r .= isset($t[$i][$j]['attributes']) ? $t[$i][$j]['attributes'] : ''; |
| 1066 | + $r .= '>'; |
| 1067 | + |
| 1068 | + $r .= $t[$i][$j]['content']; |
| 1069 | + |
| 1070 | + $r .= '</' . $t[$i][$j]['type'] . '>'; |
| 1071 | + unset($t[$i][$j]); |
993 | 1072 | } |
| 1073 | + $r .= "\n</tr>"; |
994 | 1074 | |
995 | | - $out .= "</table>\n"; |
| 1075 | + if( ( !isset($t[$i+1]) && !$simple )|| ( isset($t[$i+1]) && ($t[$i]['type'] != $t[$i+1]['type'])) ) { |
| 1076 | + $r .= '</' . $t[$i]['type'] . '>'; |
| 1077 | + } |
| 1078 | + $last_section = $t[$i]['type']; |
| 1079 | + unset($t[$i]); |
996 | 1080 | } |
997 | | - |
998 | | - # Remove trailing line-ending (b/c) |
999 | | - if ( substr( $out, -1 ) === "\n" ) { |
1000 | | - $out = substr( $out, 0, -1 ); |
| 1081 | + if ( $empty ) { |
| 1082 | + if ( isset($t['caption']) ) { |
| 1083 | + $r .= "\n<tr><td></td></tr>"; |
| 1084 | + } else { |
| 1085 | + return ''; |
| 1086 | + } |
1001 | 1087 | } |
| 1088 | + $r .= "\n</table>"; |
| 1089 | + $r .= str_repeat( '</dd></dl>' , $t['indent'] ); |
1002 | 1090 | |
1003 | | - # special case: don't return empty table |
1004 | | - if ( $out === "<table>\n<tr><td></td></tr>\n</table>" ) { |
1005 | | - $out = ''; |
1006 | | - } |
| 1091 | + return $r; |
| 1092 | + } |
1007 | 1093 | |
1008 | | - wfProfileOut( __METHOD__ ); |
1009 | | - |
1010 | | - return $out; |
| 1094 | + /** |
| 1095 | + * like end() but only works on the numeric array index and php's internal pointers |
| 1096 | + * returns a reference to the last element of an array much like "\$arr[-1]" in perl |
| 1097 | + * ignores associative elements and will create a 0 key will a NULL value if there were |
| 1098 | + * no numric elements and an array itself if not previously defined. |
| 1099 | + * |
| 1100 | + * @private |
| 1101 | + */ |
| 1102 | + function &last (&$arr) { |
| 1103 | + for($i = count($arr); (!isset($arr[$i]) && $i > 0); $i--) { } |
| 1104 | + return $arr[$i]; |
1011 | 1105 | } |
1012 | 1106 | |
1013 | 1107 | /** |
— | — | @@ -2239,7 +2333,7 @@ |
2240 | 2334 | '<td|<th|<\\/?div|<hr|<\\/pre|<\\/p|'.$this->mUniqPrefix.'-pre|<\\/li|<\\/ul|<\\/ol|<\\/?center)/iS', $t ); |
2241 | 2335 | if ( $openmatch or $closematch ) { |
2242 | 2336 | $paragraphStack = false; |
2243 | | - # TODO bug 5718: paragraph closed |
| 2337 | + # TODO bug 5718: paragraph closed |
2244 | 2338 | $output .= $this->closeParagraph(); |
2245 | 2339 | if ( $preOpenMatch and !$preCloseMatch ) { |
2246 | 2340 | $this->mInPre = true; |
Index: trunk/phase3/includes/Sanitizer.php |
— | — | @@ -369,7 +369,7 @@ |
370 | 370 | 'strike', 'strong', 'tt', 'var', 'div', 'center', |
371 | 371 | 'blockquote', 'ol', 'ul', 'dl', 'table', 'caption', 'pre', |
372 | 372 | 'ruby', 'rt' , 'rb' , 'rp', 'p', 'span', 'abbr', 'dfn', |
373 | | - 'kbd', 'samp' |
| 373 | + 'kbd', 'samp', 'thead', 'tbody', 'tfoot' |
374 | 374 | ); |
375 | 375 | $htmlsingle = array( |
376 | 376 | 'br', 'hr', 'li', 'dt', 'dd' |