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