|
2 | 2 | /* |
3 | 3 | Plugin Name: TablePress Extension: Rowspans everywhere |
4 | 4 | Description: Allows using rowspans in thead. |
5 | | -Version: 1.0.0 |
| 5 | +Version: 1.1.0 |
6 | 6 | Author: strarsis <[email protected]> |
7 | 7 | GitHub Plugin URI: strarsis/tablepress-rowspan-all |
8 | 8 | */ |
9 | 9 |
|
10 | 10 | require_once 'dom_helper.php'; |
11 | 11 |
|
12 | | -const TABLEPRESS_SPAN_OUTSIDE = '#outside_rowspan#'; |
| 12 | +const TABLEPRESS_ROWSPAN = '#rowspan#'; |
| 13 | +const TABLEPRESS_ROWSPAN_OUTSIDE = '#outside_rowspan#'; |
13 | 14 |
|
14 | | -add_filter('tablepress_datatables_parameters', function($parameters, $table_id, $html_id, $js_options ) { |
15 | | - //$parameters['orderCellsTop'] = true; |
16 | | - return $parameters; |
17 | | -}, 10, 4); |
18 | 15 |
|
19 | 16 | // 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 | + |
49 | 50 | } |
50 | | - |
51 | | - } |
52 | | - |
| 51 | + |
53 | 52 | } |
54 | | - |
55 | | - } |
56 | | - |
57 | | - return $table; |
| 53 | + |
| 54 | + return $table; |
58 | 55 | }, 10, 2); |
59 | 56 |
|
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; |
89 | 87 | } |
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); |
100 | 117 | } |
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