1<?php
2
3require_once DOKU_INC.'inc/parser/parser.php';
4
5/**
6 * All DokuWiki plugins to extend the parser/rendering mechanism
7 * need to inherit from this class
8 */
9class helper_plugin_dtable extends dokuwiki_plugin
10{
11	static $line_nr_c = array();
12	static $file_cont = NULL;
13
14    function error($code, $json=false)
15    {
16		if($json == true) {
17			return json_encode(array('type' => 'error', 'msg' => $this->getLang($code)));
18		} else {
19			return $this->getLang($code);
20		}
21    }
22
23    static function line_nr($pos, $file_path, $start_line = 0) {
24		$line_nr = 0;
25		if (!is_array(self::$line_nr_c[$file_path] ?? null)) {
26			self::$line_nr_c[$file_path] = array();
27			$start_pos = 0;
28			$line_nr = 0;
29		} else {
30			$start_pos = count(self::$line_nr_c[$file_path]) - 1;
31			$line_nr = self::$line_nr_c[$file_path][count(self::$line_nr_c[$file_path]) - 1];
32		}
33
34		if ($start_line > 0) {
35			//find last pos on current line
36			if (($find = array_search($start_line, self::$line_nr_c[$file_path] ?? [])) !== false) {
37				//the new line charter from last line -> it's nessesery in order to corect work of my handler
38				$start_pos = $find;
39				$pos += $find;
40			} else {
41				if (self::$file_cont == NULL)
42					self::$file_cont = io_readFile($file_path);
43
44				for($i = $start_pos; $i < strlen(self::$file_cont) && $line_nr < $start_line; $i++) {
45					self::$line_nr_c[$file_path][$i] = $line_nr;
46					if(self::$file_cont[$i] == "\n")
47						$line_nr++;
48				}
49				self::$line_nr_c[$file_path][$i] = $line_nr;
50
51				$pos += $i;
52				$start_pos  = $i;
53			}
54			$line_nr = $start_line;
55
56		}
57		if ($start_pos >= $pos) {
58			/*dbglog("TYRANOZAUR");
59			dbglog(self::$line_nr_c);*/
60			return self::$line_nr_c[$file_path][$pos];
61		} else {
62			if (self::$file_cont == NULL)
63				self::$file_cont = io_readFile($file_path);
64
65			for($i=$start_pos;$i <= $pos; $i++)
66			{
67				self::$line_nr_c[$file_path][$i] = $line_nr;
68				if(self::$file_cont[$i] == "\n")
69					$line_nr++;
70			}
71			return self::$line_nr_c[$file_path][$pos];
72		}
73    }
74	function rows($row, $page_id, $start_line)
75	{
76		$Parser = new Doku_Parser();
77
78		$Parser->Handler = new helper_plugin_dtable_handler($page_id, $start_line);
79
80        //add modes to parser
81		$modes = p_get_parsermodes();
82        foreach($modes as $mode) {
83            $Parser->addMode($mode['mode'], $mode['obj']);
84		}
85
86		return $Parser->parse($row);
87
88	}
89	function get_spans($start_line, $page_lines, $page_id) {
90		$table = '';
91		for ($i = $start_line; trim($page_lines[$i]) != '</dtable>' && $i < count($page_lines); $i++) {
92			$table .= $page_lines[$i]."\n";
93		}
94
95		$spans = array();
96		$rows = self::rows($table, $page_id, $start_line);
97		for ($i = 0; $i < count($rows); $i++) {
98			for ($j = 0; $j < count($rows[$i][0]); $j++) {
99				$spans[$i][$j][0] = $rows[$i][0][$j][0];
100				$spans[$i][$j][1] = $rows[$i][0][$j][1];
101			}
102		}
103		return $spans;
104	}
105    function format_row($array_line) {
106        $line = '';
107		foreach ($array_line as $cell)
108		{
109			if ($cell[0] == 'tableheader_open')
110			{
111				$line .= '^'.$cell[1];
112			} else
113			{
114				$line .= '|'.$cell[1];
115			}
116		}
117		if ($array_line[count($array_line) - 1][0] == 'tableheader_open')
118		{
119			$line .= '^';
120		} else
121		{
122			$line .= '|';
123		}
124		$line = str_replace("\n", '\\\\ ', $line);
125
126		return $line;
127	}
128    function parse_line($line, $page_id)
129    {
130		$line = preg_replace('/\s*:::\s*\|/', '', $line);
131
132
133		$info = array();
134		$html = p_render('xhtml',p_get_instructions($line),$info);
135
136		$maches = array();
137
138		preg_match('/<tr.*?>(.*?)<\/tr>/si', $html, $maches);
139
140		return trim($maches[1]);
141    }
142}
143
144class helper_plugin_dtable_handler {
145	public $calls = NULL;
146	public $row = 0;
147	public $cell = 0;
148	public $type;
149	public $file_path;
150	public $start_line;
151
152	public function __construct($page_id, $start_line) {
153		$this->file_path = wikiFN($page_id);
154		$this->start_line = $start_line;
155	}
156
157    function table($match, $state, $pos) {
158        switch ( $state ) {
159
160            case DOKU_LEXER_ENTER:
161
162				$type = trim($match);
163
164				$this->calls = array();
165
166				$line = helper_plugin_dtable::line_nr($pos, $this->file_path, $this->start_line);
167
168				$this->calls[$this->row][0][$this->cell] = array(1, 1, $type, '');
169				$this->calls[$this->row][1][0] = $line;
170
171            break;
172
173            case DOKU_LEXER_EXIT:
174				$line = helper_plugin_dtable::line_nr($pos, $this->file_path, $this->start_line);
175				$this->calls[$this->row][1][1] = $line - 1;
176
177
178            break;
179
180            case DOKU_LEXER_UNMATCHED:
181				if (is_array($this->calls)) {
182					$this->calls[$this->row][0][$this->cell][3] .= $match;
183				}
184            break;
185
186            case DOKU_LEXER_MATCHED:
187                if ( preg_match('/:::/',$match) ) {
188					$this->calls[$this->row][0][$this->cell][3] .= $match;
189				} else if ( trim($match) == '' ){
190					$this->calls[$this->row][0][$this->cell][3] .= $match;
191				} else {
192					$row = $this->row;
193					while (preg_match('/^\s*:::\s*$/', $this->calls[$row][0][$this->cell][3]) && $row > 0) {
194						$row--;
195					}
196					if ($row != $this->row)
197						$this->calls[$row][0][$this->cell][1]++;
198
199					if ( $match[0] == "\n") {
200						$line = helper_plugin_dtable::line_nr($pos, $this->file_path, $this->start_line);
201						$this->calls[$this->row][1][1] = $line - 1;
202
203						//remove last cell and -- the celsapn it doesn't exist
204						array_pop($this->calls[$this->row][0]);
205
206						$this->row++;
207						$this->calls[$this->row] = array(array(), array());
208
209						$this->cell = 0;
210						$type = $match[1];
211
212						$this->calls[$this->row][1][0] = $line;
213
214						$this->calls[$this->row][0][$this->cell] = array(1, 1, $type, '');
215					} else {
216						if ($this->calls[$this->row][0][$this->cell][3] == '' && $this->cell > 0) {
217							$this->calls[$this->row][0][$this->cell - 1][0]++;
218							array_pop($this->calls[$this->row][0]);
219						} else {
220							$this->cell++;
221						}
222						$type = $match[0];
223						$this->calls[$this->row][0][$this->cell] = array(1, 1, $type, '');
224
225					}
226				}
227            break;
228        }
229        return true;
230    }
231	/** Change // into \n during editing in textbox, can be turn off if not needed. */
232    public function linebreak($match, $state, $pos) {
233		$this->calls[$this->row][0][$this->cell][3] .= "\n";
234        return true;
235    }
236    /**
237	* Catchall handler for the remaining syntax
238	*
239	* @param string $name Function name that was called
240	* @param array $params Original parameters
241	* @return bool If parsing should be continue
242	*/
243    public function __call($name, $params) {
244        if($this->calls === null) $this->calls = [];
245        if(!isset($this->calls[$this->row][0][$this->cell][3])) {
246            $this->calls[$this->row][0][$this->cell][3] = '';
247        }
248		$this->calls[$this->row][0][$this->cell][3] .= $params[0];
249		return true;
250    }
251	public function _finalize() {
252		//remove last cell and -- the celsapn it doesn't exist
253		array_pop($this->calls[$this->row][0]);
254		$this->row = 0;
255		$this->cell = 0;
256	}
257}
258
259