Инструменты пользователя

Инструменты сайта


wiki:xref:dokuwiki:inc:parsing:handler:table.php
Table.php
  1. <?php
  2.  
  3. namespace dokuwiki\Parsing\Handler;
  4.  
  5. class Table extends AbstractRewriter
  6. {
  7.  
  8. protected $tableCalls = array();
  9. protected $maxCols = 0;
  10. protected $maxRows = 1;
  11. protected $currentCols = 0;
  12. protected $firstCell = false;
  13. protected $lastCellType = 'tablecell';
  14. protected $inTableHead = true;
  15. protected $currentRow = array('tableheader' => 0, 'tablecell' => 0);
  16. protected $countTableHeadRows = 0;
  17.  
  18. /** @inheritdoc */
  19. public function finalise()
  20. {
  21. $last_call = end($this->calls);
  22. $this->writeCall(array('table_end',array(), $last_call[2]));
  23.  
  24. $this->process();
  25. $this->callWriter->finalise();
  26. unset($this->callWriter);
  27. }
  28.  
  29. /** @inheritdoc */
  30. public function process()
  31. {
  32. foreach ($this->calls as $call) {
  33. switch ($call[0]) {
  34. case 'table_start':
  35. $this->tableStart($call);
  36. break;
  37. case 'table_row':
  38. $this->tableRowClose($call);
  39. $this->tableRowOpen(array('tablerow_open',$call[1],$call[2]));
  40. break;
  41. case 'tableheader':
  42. case 'tablecell':
  43. $this->tableCell($call);
  44. break;
  45. case 'table_end':
  46. $this->tableRowClose($call);
  47. $this->tableEnd($call);
  48. break;
  49. default:
  50. $this->tableDefault($call);
  51. break;
  52. }
  53. }
  54. $this->callWriter->writeCalls($this->tableCalls);
  55.  
  56. return $this->callWriter;
  57. }
  58.  
  59. protected function tableStart($call)
  60. {
  61. $this->tableCalls[] = array('table_open',$call[1],$call[2]);
  62. $this->tableCalls[] = array('tablerow_open',array(),$call[2]);
  63. $this->firstCell = true;
  64. }
  65.  
  66. protected function tableEnd($call)
  67. {
  68. $this->tableCalls[] = array('table_close',$call[1],$call[2]);
  69. $this->finalizeTable();
  70. }
  71.  
  72. protected function tableRowOpen($call)
  73. {
  74. $this->tableCalls[] = $call;
  75. $this->currentCols = 0;
  76. $this->firstCell = true;
  77. $this->lastCellType = 'tablecell';
  78. $this->maxRows++;
  79. if ($this->inTableHead) {
  80. $this->currentRow = array('tablecell' => 0, 'tableheader' => 0);
  81. }
  82. }
  83.  
  84. protected function tableRowClose($call)
  85. {
  86. if ($this->inTableHead && ($this->inTableHead = $this->isTableHeadRow())) {
  87. $this->countTableHeadRows++;
  88. }
  89. // Strip off final cell opening and anything after it
  90. while ($discard = array_pop($this->tableCalls)) {
  91. if ($discard[0] == 'tablecell_open' || $discard[0] == 'tableheader_open') {
  92. break;
  93. }
  94. if (!empty($this->currentRow[$discard[0]])) {
  95. $this->currentRow[$discard[0]]--;
  96. }
  97. }
  98. $this->tableCalls[] = array('tablerow_close', array(), $call[2]);
  99.  
  100. if ($this->currentCols > $this->maxCols) {
  101. $this->maxCols = $this->currentCols;
  102. }
  103. }
  104.  
  105. protected function isTableHeadRow()
  106. {
  107. $td = $this->currentRow['tablecell'];
  108. $th = $this->currentRow['tableheader'];
  109.  
  110. if (!$th || $td > 2) return false;
  111. if (2*$td > $th) return false;
  112.  
  113. return true;
  114. }
  115.  
  116. protected function tableCell($call)
  117. {
  118. if ($this->inTableHead) {
  119. $this->currentRow[$call[0]]++;
  120. }
  121. if (!$this->firstCell) {
  122. // Increase the span
  123. $lastCall = end($this->tableCalls);
  124.  
  125. // A cell call which follows an open cell means an empty cell so span
  126. if ($lastCall[0] == 'tablecell_open' || $lastCall[0] == 'tableheader_open') {
  127. $this->tableCalls[] = array('colspan',array(),$call[2]);
  128. }
  129.  
  130. $this->tableCalls[] = array($this->lastCellType.'_close',array(),$call[2]);
  131. $this->tableCalls[] = array($call[0].'_open',array(1,null,1),$call[2]);
  132. $this->lastCellType = $call[0];
  133. } else {
  134. $this->tableCalls[] = array($call[0].'_open',array(1,null,1),$call[2]);
  135. $this->lastCellType = $call[0];
  136. $this->firstCell = false;
  137. }
  138.  
  139. $this->currentCols++;
  140. }
  141.  
  142. protected function tableDefault($call)
  143. {
  144. $this->tableCalls[] = $call;
  145. }
  146.  
  147. protected function finalizeTable()
  148. {
  149.  
  150. // Add the max cols and rows to the table opening
  151. if ($this->tableCalls[0][0] == 'table_open') {
  152. // Adjust to num cols not num col delimeters
  153. $this->tableCalls[0][1][] = $this->maxCols - 1;
  154. $this->tableCalls[0][1][] = $this->maxRows;
  155. $this->tableCalls[0][1][] = array_shift($this->tableCalls[0][1]);
  156. } else {
  157. trigger_error('First element in table call list is not table_open');
  158. }
  159.  
  160. $lastRow = 0;
  161. $lastCell = 0;
  162. $cellKey = array();
  163. $toDelete = array();
  164.  
  165. // if still in tableheader, then there can be no table header
  166. // as all rows can't be within <THEAD>
  167. if ($this->inTableHead) {
  168. $this->inTableHead = false;
  169. $this->countTableHeadRows = 0;
  170. }
  171.  
  172. // Look for the colspan elements and increment the colspan on the
  173. // previous non-empty opening cell. Once done, delete all the cells
  174. // that contain colspans
  175. for ($key = 0; $key < count($this->tableCalls); ++$key) {
  176. $call = $this->tableCalls[$key];
  177.  
  178. switch ($call[0]) {
  179. case 'table_open':
  180. if ($this->countTableHeadRows) {
  181. array_splice($this->tableCalls, $key+1, 0, array(
  182. array('tablethead_open', array(), $call[2])));
  183. }
  184. break;
  185.  
  186. case 'tablerow_open':
  187. $lastRow++;
  188. $lastCell = 0;
  189. break;
  190.  
  191. case 'tablecell_open':
  192. case 'tableheader_open':
  193. $lastCell++;
  194. $cellKey[$lastRow][$lastCell] = $key;
  195. break;
  196.  
  197. case 'table_align':
  198. $prev = in_array($this->tableCalls[$key-1][0], array('tablecell_open', 'tableheader_open'));
  199. $next = in_array($this->tableCalls[$key+1][0], array('tablecell_close', 'tableheader_close'));
  200. // If the cell is empty, align left
  201. if ($prev && $next) {
  202. $this->tableCalls[$key-1][1][1] = 'left';
  203.  
  204. // If the previous element was a cell open, align right
  205. } elseif ($prev) {
  206. $this->tableCalls[$key-1][1][1] = 'right';
  207.  
  208. // If the next element is the close of an element, align either center or left
  209. } elseif ($next) {
  210. if ($this->tableCalls[$cellKey[$lastRow][$lastCell]][1][1] == 'right') {
  211. $this->tableCalls[$cellKey[$lastRow][$lastCell]][1][1] = 'center';
  212. } else {
  213. $this->tableCalls[$cellKey[$lastRow][$lastCell]][1][1] = 'left';
  214. }
  215. }
  216.  
  217. // Now convert the whitespace back to cdata
  218. $this->tableCalls[$key][0] = 'cdata';
  219. break;
  220.  
  221. case 'colspan':
  222. $this->tableCalls[$key-1][1][0] = false;
  223.  
  224. for ($i = $key-2; $i >= $cellKey[$lastRow][1]; $i--) {
  225. if ($this->tableCalls[$i][0] == 'tablecell_open' ||
  226. $this->tableCalls[$i][0] == 'tableheader_open'
  227. ) {
  228. if (false !== $this->tableCalls[$i][1][0]) {
  229. $this->tableCalls[$i][1][0]++;
  230. break;
  231. }
  232. }
  233. }
  234.  
  235. $toDelete[] = $key-1;
  236. $toDelete[] = $key;
  237. $toDelete[] = $key+1;
  238. break;
  239.  
  240. case 'rowspan':
  241. if ($this->tableCalls[$key-1][0] == 'cdata') {
  242. // ignore rowspan if previous call was cdata (text mixed with :::)
  243. // we don't have to check next call as that wont match regex
  244. $this->tableCalls[$key][0] = 'cdata';
  245. } else {
  246. $spanning_cell = null;
  247.  
  248. // can't cross thead/tbody boundary
  249. if (!$this->countTableHeadRows || ($lastRow-1 != $this->countTableHeadRows)) {
  250. for ($i = $lastRow-1; $i > 0; $i--) {
  251. if ($this->tableCalls[$cellKey[$i][$lastCell]][0] == 'tablecell_open' ||
  252. $this->tableCalls[$cellKey[$i][$lastCell]][0] == 'tableheader_open'
  253. ) {
  254. if ($this->tableCalls[$cellKey[$i][$lastCell]][1][2] >= $lastRow - $i) {
  255. $spanning_cell = $i;
  256. break;
  257. }
  258. }
  259. }
  260. }
  261. if (is_null($spanning_cell)) {
  262. // No spanning cell found, so convert this cell to
  263. // an empty one to avoid broken tables
  264. $this->tableCalls[$key][0] = 'cdata';
  265. $this->tableCalls[$key][1][0] = '';
  266. break;
  267. }
  268. $this->tableCalls[$cellKey[$spanning_cell][$lastCell]][1][2]++;
  269.  
  270. $this->tableCalls[$key-1][1][2] = false;
  271.  
  272. $toDelete[] = $key-1;
  273. $toDelete[] = $key;
  274. $toDelete[] = $key+1;
  275. }
  276. break;
  277.  
  278. case 'tablerow_close':
  279. // Fix broken tables by adding missing cells
  280. $moreCalls = array();
  281. while (++$lastCell < $this->maxCols) {
  282. $moreCalls[] = array('tablecell_open', array(1, null, 1), $call[2]);
  283. $moreCalls[] = array('cdata', array(''), $call[2]);
  284. $moreCalls[] = array('tablecell_close', array(), $call[2]);
  285. }
  286. $moreCallsLength = count($moreCalls);
  287. if ($moreCallsLength) {
  288. array_splice($this->tableCalls, $key, 0, $moreCalls);
  289. $key += $moreCallsLength;
  290. }
  291.  
  292. if ($this->countTableHeadRows == $lastRow) {
  293. array_splice($this->tableCalls, $key+1, 0, array(
  294. array('tablethead_close', array(), $call[2])));
  295. }
  296. break;
  297. }
  298. }
  299.  
  300. // condense cdata
  301. $cnt = count($this->tableCalls);
  302. for ($key = 0; $key < $cnt; $key++) {
  303. if ($this->tableCalls[$key][0] == 'cdata') {
  304. $ckey = $key;
  305. $key++;
  306. while ($this->tableCalls[$key][0] == 'cdata') {
  307. $this->tableCalls[$ckey][1][0] .= $this->tableCalls[$key][1][0];
  308. $toDelete[] = $key;
  309. $key++;
  310. }
  311. continue;
  312. }
  313. }
  314.  
  315. foreach ($toDelete as $delete) {
  316. unset($this->tableCalls[$delete]);
  317. }
  318. $this->tableCalls = array_values($this->tableCalls);
  319. }
  320. }
Только авторизованные участники могут оставлять комментарии.
wiki/xref/dokuwiki/inc/parsing/handler/table.php.txt · Последнее изменение: 127.0.0.1