Skip to content

Commit 9abde20

Browse files
committed
Add fix for table header cells (first row cells) with colspan.
1 parent 8a552a1 commit 9abde20

File tree

1 file changed

+113
-104
lines changed

1 file changed

+113
-104
lines changed

tablepress-rowspan-all.php

Lines changed: 113 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -2,122 +2,131 @@
22
/*
33
Plugin Name: TablePress Extension: Rowspans everywhere
44
Description: Allows using rowspans in thead.
5-
Version: 1.0.0
5+
Version: 1.1.0
66
Author: strarsis <[email protected]>
77
GitHub Plugin URI: strarsis/tablepress-rowspan-all
88
*/
99

1010
require_once 'dom_helper.php';
1111

12-
const TABLEPRESS_SPAN_OUTSIDE = '#outside_rowspan#';
12+
const TABLEPRESS_ROWSPAN = '#rowspan#';
13+
const TABLEPRESS_ROWSPAN_OUTSIDE = '#outside_rowspan#';
1314

14-
add_filter('tablepress_datatables_parameters', function($parameters, $table_id, $html_id, $js_options ) {
15-
//$parameters['orderCellsTop'] = true;
16-
return $parameters;
17-
}, 10, 4);
1815

1916
// change trigger words in table header to preserve them
20-
add_filter('tablepress_table_raw_render_data', function($table, $render_options) {
21-
22-
// rows
23-
$rows = $table['data'];
24-
25-
// more than two row for thead + potential rowspan making sense at all
26-
if(count($rows) <= 1) {
27-
return $table; // skip
28-
}
29-
30-
// with a tfoot, there must be at least three rows (header, middle, footer)
31-
if( $render_options['table_head'] &&
32-
(!$render_options['table_foot'] || $render_options['table_foot'] && count($rows) <= 2)) {
33-
34-
$last_row_no = 0;
35-
for($row_no = 1; $row_no < count($rows); $row_no++) {
36-
$row = $rows[$row_no];
37-
38-
for($col_no = 0; $col_no < count($row); $col_no++) {
39-
$col = $row[$col_no];
40-
41-
// only continous rows after head
42-
if($col == '#rowspan#') {
43-
if($row_no > ($last_row_no+1)) {
44-
return $table; // skip table
45-
}
46-
$last_row_no = $row_no;
47-
48-
$table['data'][$row_no][$col_no] = TABLEPRESS_SPAN_OUTSIDE;
17+
// (TablePress already uses #rowspan# but only between body cells, not table head + body cells)
18+
add_filter('tablepress_table_raw_render_data', function($table, $render_options)
19+
{
20+
21+
// rows
22+
$rows = $table['data'];
23+
24+
// more than two row for thead + potential rowspan making sense at all
25+
if (count($rows) <= 1)
26+
return $table; // skip
27+
28+
// with a tfoot, there must be at least three rows (header, middle, footer)
29+
if ($render_options['table_head'] && (!$render_options['table_foot'] || $render_options['table_foot'] && count($rows) <= 2)) {
30+
31+
$last_row_no = 0;
32+
for ($row_no = 1; $row_no < count($rows); $row_no++) {
33+
$row = $rows[$row_no];
34+
35+
for ($col_no = 0; $col_no < count($row); $col_no++) {
36+
$col = $row[$col_no];
37+
38+
// only continous rows after head
39+
if ($col === TABLEPRESS_ROWSPAN) {
40+
if ($row_no > ($last_row_no + 1))
41+
return $table; // skip table
42+
43+
$last_row_no = $row_no;
44+
45+
$table['data'][$row_no][$col_no] = TABLEPRESS_ROWSPAN_OUTSIDE;
46+
}
47+
48+
}
49+
4950
}
50-
51-
}
52-
51+
5352
}
54-
55-
}
56-
57-
return $table;
53+
54+
return $table;
5855
}, 10, 2);
5956

60-
// adjust tablepress markup by using the previously prepared trigger words
61-
add_filter('tablepress_table_output', function($output, $table, $render_options) {
62-
63-
// table
64-
$table = fragment_to_dom($output);
65-
$dom = $table->ownerDocument;
66-
$xpath = new DOMXpath($dom);
67-
68-
// thead
69-
$theads = $xpath->query("//thead");
70-
if($theads->length == 0) {
71-
return $output; // skip
72-
}
73-
// thead rows + cols
74-
$thead = $theads->item(0);
75-
$thead_row = $dom->getElementsByTagName('thead')->item(0);
76-
$thead_cells = $dom->getElementsByTagName('th');
77-
78-
// all #head_rowspan# cells
79-
$trigger_cells = $xpath->query(".//td[not(ancestor::thead) and not(ancestor::tfoot) and text() = '" . TABLEPRESS_SPAN_OUTSIDE . "']");
80-
$last_row_no = 0;
81-
foreach($trigger_cells as $cell) {
82-
// row of cell
83-
$row = $xpath->query("parent::tr", $cell)->item(0);
84-
85-
// only continous rows after head (2nd test)
86-
$row_no = dom_parent_position($row);
87-
if($row_no > ($last_row_no+1)) {
88-
continue;
57+
add_filter('tablepress_table_output', function($output, $table, $render_options)
58+
{
59+
// table
60+
$table = fragment_to_dom($output);
61+
$dom = $table->ownerDocument;
62+
$xpath = new DOMXpath($dom);
63+
64+
// tablepress-specific assumptions:
65+
// - there is only one row (<tr></tr>) in table header (<thead></thead>)
66+
67+
// thead
68+
$theads = $xpath->query('//thead');
69+
if ($theads->length === 0)
70+
return $output; // skip
71+
$thead = $theads->item(0);
72+
73+
$thead_row = $thead->getElementsByTagName('tr')->item(0);
74+
$thead_cells = $thead_row->getElementsByTagName('th');
75+
76+
// thead cells can also have colspan
77+
// physical occupation map
78+
$thead_cells_phys = array();
79+
foreach ($thead_cells as $thead_cell_index => $thead_cell) {
80+
$thead_cells_phys[] = $thead_cell_index; // add at least once
81+
82+
$thead_cell_colspan = $thead_cell->getAttribute('colspan');
83+
if (empty($thead_cell_colspan))
84+
continue; // skip cell
85+
for ($cs = 0; $cs < ($thead_cell_colspan - 1); $cs++)
86+
$thead_cells_phys[] = $thead_cell_index;
8987
}
90-
$last_row_no = $row_no;
91-
92-
// find thead cell above this cell
93-
$cell_no = dom_parent_position($cell);
94-
$thead_cell = $thead_cells->item($cell_no);
95-
96-
// update rowspan of cell
97-
$cur_rowspan = $thead_cell->getAttribute("rowspan");
98-
if(!$cur_rowspan) {
99-
$cur_rowspan = 1;
88+
89+
// all rowspan marked cells
90+
$trigger_cells = $xpath->query(".//td[not(ancestor::thead) and " . "not(ancestor::tfoot) and " . "text() = '" . TABLEPRESS_ROWSPAN_OUTSIDE . "']");
91+
92+
$last_row_no = 0;
93+
foreach ($trigger_cells as $cell) {
94+
// row of cell
95+
$row = $xpath->query('parent::tr', $cell)->item(0);
96+
97+
// only continous rows after head (2nd test)
98+
$row_no = dom_parent_position($row);
99+
if ($row_no > ($last_row_no + 1))
100+
continue;
101+
$last_row_no = $row_no;
102+
103+
// find thead cell above this cell
104+
$cell_no = dom_parent_position($cell);
105+
$thead_cell_phys_no = $thead_cells_phys[$cell_no];
106+
$thead_cell = $thead_cells->item($thead_cell_phys_no);
107+
108+
// update rowspan of cell
109+
$cur_rowspan = $thead_cell->getAttribute('rowspan');
110+
if (!$cur_rowspan)
111+
$cur_rowspan = 1;
112+
113+
$thead_cell->setAttribute('rowspan', $cur_rowspan + 1);
114+
115+
// move row to thead
116+
$thead->appendChild($row);
100117
}
101-
$thead_cell->setAttribute("rowspan", $cur_rowspan + 1);
102-
103-
// move row to thead
104-
$thead->appendChild($row);
105-
}
106-
107-
// remove the placeholder trigger word cells
108-
// in second step because removal during iteration causes issues
109-
foreach( $trigger_cells as $cell ) {
110-
$cell->parentNode->removeChild($cell);
111-
}
112-
113-
// change td to th for the new row
114-
$thead_td_cells = $xpath->query('.//td', $thead);
115-
foreach($thead_td_cells as $td_cell) {
116-
$changed_th_cell = dom_change_tagname($td_cell, "th");
117-
}
118-
119-
120-
$newHtml = dom_to_fragment($table);
121-
return $newHtml;
122-
123-
}, 10, 3);
118+
119+
// remove the placeholder trigger word cells
120+
// in second step because removal during iteration causes issues
121+
foreach ($trigger_cells as $cell)
122+
$cell->parentNode->removeChild($cell);
123+
124+
// change td to th for the new row
125+
$thead_td_cells = $xpath->query('.//td', $thead);
126+
foreach ($thead_td_cells as $td_cell)
127+
$changed_th_cell = dom_change_tagname($td_cell, 'th');
128+
129+
130+
$newHtml = dom_to_fragment($table);
131+
return $newHtml;
132+
}, 3, 10);

0 commit comments

Comments
 (0)