Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 273 |
|
0.00% |
0 / 52 |
CRAP | |
0.00% |
0 / 5 |
diff_renderer | |
0.00% |
0 / 83 |
|
0.00% |
0 / 15 |
1892 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 | |||
get_params | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 | |||
render | |
0.00% |
0 / 38 |
|
0.00% |
0 / 1 |
182 | |||
_block | |
0.00% |
0 / 16 |
|
0.00% |
0 / 1 |
42 | |||
_start_diff | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
_end_diff | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
_block_header | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
72 | |||
_start_block | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
_end_block | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
_lines | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
_context | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
_added | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
_deleted | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
_changed | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
get_diff_content | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
diff_renderer_unified | |
0.00% |
0 / 14 |
|
0.00% |
0 / 9 |
132 | |
0.00% |
0 / 1 |
get_diff_content | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
_block_header | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 | |||
_context | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
_added | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
_deleted | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
_changed | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
_start_diff | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
_end_diff | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
_end_block | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
diff_renderer_inline | |
0.00% |
0 / 51 |
|
0.00% |
0 / 11 |
462 | |
0.00% |
0 / 1 |
get_diff_content | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
_start_diff | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
_end_diff | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
_block_header | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
_start_block | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
_lines | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 | |||
_added | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
_deleted | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
_changed | |
0.00% |
0 / 16 |
|
0.00% |
0 / 1 |
42 | |||
_split_on_words | |
0.00% |
0 / 16 |
|
0.00% |
0 / 1 |
20 | |||
_encode | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
diff_renderer_raw | |
0.00% |
0 / 10 |
|
0.00% |
0 / 6 |
72 | |
0.00% |
0 / 1 |
get_diff_content | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
_block_header | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 | |||
_context | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
_added | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
_deleted | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
_changed | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
diff_renderer_side_by_side | |
0.00% |
0 / 113 |
|
0.00% |
0 / 11 |
2256 | |
0.00% |
0 / 1 |
get_diff_content | |
0.00% |
0 / 65 |
|
0.00% |
0 / 1 |
702 | |||
_start_diff | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
2 | |||
_end_diff | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
30 | |||
_block_header | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
_added | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
_perform_add | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
20 | |||
_deleted | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
_perform_delete | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
_context | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
_perform_context | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
20 | |||
_changed | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | /** |
3 | * |
4 | * This file is part of the phpBB Forum Software package. |
5 | * |
6 | * @copyright (c) phpBB Limited <https://www.phpbb.com> |
7 | * @license GNU General Public License, version 2 (GPL-2.0) |
8 | * |
9 | * For full copyright and license information, please see |
10 | * the docs/CREDITS.txt file. |
11 | * |
12 | */ |
13 | |
14 | /** |
15 | * @ignore |
16 | */ |
17 | if (!defined('IN_PHPBB')) |
18 | { |
19 | exit; |
20 | } |
21 | |
22 | /** |
23 | * Code from pear.php.net, Text_Diff-1.1.0 package |
24 | * http://pear.php.net/package/Text_Diff/ |
25 | * |
26 | * Modified by phpBB Limited to meet our coding standards |
27 | * and being able to integrate into phpBB |
28 | * |
29 | * A class to render Diffs in different formats. |
30 | * |
31 | * This class renders the diff in classic diff format. It is intended that |
32 | * this class be customized via inheritance, to obtain fancier outputs. |
33 | * |
34 | * Copyright 2004-2008 The Horde Project (http://www.horde.org/) |
35 | * |
36 | * @package diff |
37 | */ |
38 | class diff_renderer |
39 | { |
40 | /** |
41 | * Number of leading context "lines" to preserve. |
42 | * |
43 | * This should be left at zero for this class, but subclasses may want to |
44 | * set this to other values. |
45 | */ |
46 | var $_leading_context_lines = 0; |
47 | |
48 | /** |
49 | * Number of trailing context "lines" to preserve. |
50 | * |
51 | * This should be left at zero for this class, but subclasses may want to |
52 | * set this to other values. |
53 | */ |
54 | var $_trailing_context_lines = 0; |
55 | |
56 | /** |
57 | * Constructor. |
58 | */ |
59 | function __construct($params = array()) |
60 | { |
61 | foreach ($params as $param => $value) |
62 | { |
63 | $v = '_' . $param; |
64 | if (isset($this->$v)) |
65 | { |
66 | $this->$v = $value; |
67 | } |
68 | } |
69 | } |
70 | |
71 | /** |
72 | * Get any renderer parameters. |
73 | * |
74 | * @return array All parameters of this renderer object. |
75 | */ |
76 | function get_params() |
77 | { |
78 | $params = array(); |
79 | foreach (get_object_vars($this) as $k => $v) |
80 | { |
81 | if ($k[0] == '_') |
82 | { |
83 | $params[substr($k, 1)] = $v; |
84 | } |
85 | } |
86 | |
87 | return $params; |
88 | } |
89 | |
90 | /** |
91 | * Renders a diff. |
92 | * |
93 | * @param diff &$diff A diff object. |
94 | * |
95 | * @return string The formatted output. |
96 | */ |
97 | function render(&$diff) |
98 | { |
99 | $xi = $yi = 1; |
100 | $block = false; |
101 | $context = array(); |
102 | |
103 | // Create a new diff object if it is a 3-way diff |
104 | if (is_a($diff, 'diff3')) |
105 | { |
106 | $diff3 = &$diff; |
107 | |
108 | $diff_1 = $diff3->get_original(); |
109 | $diff_2 = $diff3->merged_output(); |
110 | |
111 | unset($diff3); |
112 | |
113 | $diff = new diff($diff_1, $diff_2); |
114 | } |
115 | |
116 | $nlead = $this->_leading_context_lines; |
117 | $ntrail = $this->_trailing_context_lines; |
118 | |
119 | $output = $this->_start_diff(); |
120 | $diffs = $diff->get_diff(); |
121 | |
122 | foreach ($diffs as $i => $edit) |
123 | { |
124 | // If these are unchanged (copied) lines, and we want to keep leading or trailing context lines, extract them from the copy block. |
125 | if (is_a($edit, 'diff_op_copy')) |
126 | { |
127 | // Do we have any diff blocks yet? |
128 | if (is_array($block)) |
129 | { |
130 | // How many lines to keep as context from the copy block. |
131 | $keep = ($i == count($diffs) - 1) ? $ntrail : $nlead + $ntrail; |
132 | if (count($edit->orig) <= $keep) |
133 | { |
134 | // We have less lines in the block than we want for context => keep the whole block. |
135 | $block[] = $edit; |
136 | } |
137 | else |
138 | { |
139 | if ($ntrail) |
140 | { |
141 | // Create a new block with as many lines as we need for the trailing context. |
142 | $context = array_slice($edit->orig, 0, $ntrail); |
143 | $block[] = new diff_op_copy($context); |
144 | } |
145 | |
146 | $output .= $this->_block($x0, $ntrail + $xi - $x0, $y0, $ntrail + $yi - $y0, $block); |
147 | $block = false; |
148 | } |
149 | } |
150 | // Keep the copy block as the context for the next block. |
151 | $context = $edit->orig; |
152 | } |
153 | else |
154 | { |
155 | // Don't we have any diff blocks yet? |
156 | if (!is_array($block)) |
157 | { |
158 | // Extract context lines from the preceding copy block. |
159 | $context = array_slice($context, count($context) - $nlead); |
160 | $x0 = $xi - count($context); |
161 | $y0 = $yi - count($context); |
162 | $block = array(); |
163 | |
164 | if ($context) |
165 | { |
166 | $block[] = new diff_op_copy($context); |
167 | } |
168 | } |
169 | $block[] = $edit; |
170 | } |
171 | |
172 | $xi += ($edit->orig) ? count($edit->orig) : 0; |
173 | $yi += ($edit->final) ? count($edit->final) : 0; |
174 | } |
175 | |
176 | if (is_array($block)) |
177 | { |
178 | $output .= $this->_block($x0, $xi - $x0, $y0, $yi - $y0, $block); |
179 | } |
180 | |
181 | return $output . $this->_end_diff(); |
182 | } |
183 | |
184 | function _block($xbeg, $xlen, $ybeg, $ylen, &$edits) |
185 | { |
186 | $output = $this->_start_block($this->_block_header($xbeg, $xlen, $ybeg, $ylen)); |
187 | |
188 | foreach ($edits as $edit) |
189 | { |
190 | switch (get_class($edit)) |
191 | { |
192 | case 'diff_op_copy': |
193 | $output .= $this->_context($edit->orig); |
194 | break; |
195 | |
196 | case 'diff_op_add': |
197 | $output .= $this->_added($edit->final); |
198 | break; |
199 | |
200 | case 'diff_op_delete': |
201 | $output .= $this->_deleted($edit->orig); |
202 | break; |
203 | |
204 | case 'diff_op_change': |
205 | $output .= $this->_changed($edit->orig, $edit->final); |
206 | break; |
207 | } |
208 | } |
209 | |
210 | return $output . $this->_end_block(); |
211 | } |
212 | |
213 | function _start_diff() |
214 | { |
215 | return ''; |
216 | } |
217 | |
218 | function _end_diff() |
219 | { |
220 | return ''; |
221 | } |
222 | |
223 | function _block_header($xbeg, $xlen, $ybeg, $ylen) |
224 | { |
225 | if ($xlen > 1) |
226 | { |
227 | $xbeg .= ',' . ($xbeg + $xlen - 1); |
228 | } |
229 | |
230 | if ($ylen > 1) |
231 | { |
232 | $ybeg .= ',' . ($ybeg + $ylen - 1); |
233 | } |
234 | |
235 | // this matches the GNU Diff behaviour |
236 | if ($xlen && !$ylen) |
237 | { |
238 | $ybeg--; |
239 | } |
240 | else if (!$xlen) |
241 | { |
242 | $xbeg--; |
243 | } |
244 | |
245 | return $xbeg . ($xlen ? ($ylen ? 'c' : 'd') : 'a') . $ybeg; |
246 | } |
247 | |
248 | function _start_block($header) |
249 | { |
250 | return $header . "\n"; |
251 | } |
252 | |
253 | function _end_block() |
254 | { |
255 | return ''; |
256 | } |
257 | |
258 | function _lines($lines, $prefix = ' ') |
259 | { |
260 | return $prefix . implode("\n$prefix", $lines) . "\n"; |
261 | } |
262 | |
263 | function _context($lines) |
264 | { |
265 | return $this->_lines($lines, ' '); |
266 | } |
267 | |
268 | function _added($lines) |
269 | { |
270 | return $this->_lines($lines, '> '); |
271 | } |
272 | |
273 | function _deleted($lines) |
274 | { |
275 | return $this->_lines($lines, '< '); |
276 | } |
277 | |
278 | function _changed($orig, $final) |
279 | { |
280 | return $this->_deleted($orig) . "---\n" . $this->_added($final); |
281 | } |
282 | |
283 | /** |
284 | * Our function to get the diff |
285 | */ |
286 | function get_diff_content($diff) |
287 | { |
288 | return $this->render($diff); |
289 | } |
290 | } |
291 | |
292 | /** |
293 | * Renders a unified diff |
294 | * @package diff |
295 | */ |
296 | class diff_renderer_unified extends diff_renderer |
297 | { |
298 | var $_leading_context_lines = 4; |
299 | var $_trailing_context_lines = 4; |
300 | |
301 | /** |
302 | * Our function to get the diff |
303 | */ |
304 | function get_diff_content($diff) |
305 | { |
306 | return nl2br($this->render($diff)); |
307 | } |
308 | |
309 | function _block_header($xbeg, $xlen, $ybeg, $ylen) |
310 | { |
311 | if ($xlen != 1) |
312 | { |
313 | $xbeg .= ',' . $xlen; |
314 | } |
315 | |
316 | if ($ylen != 1) |
317 | { |
318 | $ybeg .= ',' . $ylen; |
319 | } |
320 | return '<div class="diff"><big class="info">@@ -' . $xbeg . ' +' . $ybeg . ' @@</big></div>'; |
321 | } |
322 | |
323 | function _context($lines) |
324 | { |
325 | return '<pre class="diff context">' . htmlspecialchars($this->_lines($lines, ' '), ENT_COMPAT) . '<br /></pre>'; |
326 | } |
327 | |
328 | function _added($lines) |
329 | { |
330 | return '<pre class="diff added">' . htmlspecialchars($this->_lines($lines, '+'), ENT_COMPAT) . '<br /></pre>'; |
331 | } |
332 | |
333 | function _deleted($lines) |
334 | { |
335 | return '<pre class="diff removed">' . htmlspecialchars($this->_lines($lines, '-'), ENT_COMPAT) . '<br /></pre>'; |
336 | } |
337 | |
338 | function _changed($orig, $final) |
339 | { |
340 | return $this->_deleted($orig) . $this->_added($final); |
341 | } |
342 | |
343 | function _start_diff() |
344 | { |
345 | $start = '<div class="file">'; |
346 | |
347 | return $start; |
348 | } |
349 | |
350 | function _end_diff() |
351 | { |
352 | return '</div>'; |
353 | } |
354 | |
355 | function _end_block() |
356 | { |
357 | return ''; |
358 | } |
359 | } |
360 | |
361 | /** |
362 | * "Inline" diff renderer. |
363 | * |
364 | * This class renders diffs in the Wiki-style "inline" format. |
365 | * |
366 | * @author Ciprian Popovici |
367 | * @package diff |
368 | */ |
369 | class diff_renderer_inline extends diff_renderer |
370 | { |
371 | var $_leading_context_lines = 10000; |
372 | var $_trailing_context_lines = 10000; |
373 | |
374 | // Prefix and suffix for inserted text |
375 | var $_ins_prefix = '<span class="ins">'; |
376 | var $_ins_suffix = '</span>'; |
377 | |
378 | // Prefix and suffix for deleted text |
379 | var $_del_prefix = '<span class="del">'; |
380 | var $_del_suffix = '</span>'; |
381 | |
382 | var $_block_head = ''; |
383 | |
384 | // What are we currently splitting on? Used to recurse to show word-level |
385 | var $_split_level = 'lines'; |
386 | |
387 | /** |
388 | * Our function to get the diff |
389 | */ |
390 | function get_diff_content($diff) |
391 | { |
392 | return '<pre>' . nl2br($this->render($diff)) . '<br /></pre>'; |
393 | } |
394 | |
395 | function _start_diff() |
396 | { |
397 | return ''; |
398 | } |
399 | |
400 | function _end_diff() |
401 | { |
402 | return ''; |
403 | } |
404 | |
405 | function _block_header($xbeg, $xlen, $ybeg, $ylen) |
406 | { |
407 | return $this->_block_head; |
408 | } |
409 | |
410 | function _start_block($header) |
411 | { |
412 | return $header; |
413 | } |
414 | |
415 | function _lines($lines, $prefix = ' ', $encode = true) |
416 | { |
417 | if ($encode) |
418 | { |
419 | array_walk($lines, array(&$this, '_encode')); |
420 | } |
421 | |
422 | if ($this->_split_level == 'words') |
423 | { |
424 | return implode('', $lines); |
425 | } |
426 | else |
427 | { |
428 | return implode("\n", $lines) . "\n"; |
429 | } |
430 | } |
431 | |
432 | function _added($lines) |
433 | { |
434 | array_walk($lines, array(&$this, '_encode')); |
435 | $lines[0] = $this->_ins_prefix . $lines[0]; |
436 | $lines[count($lines) - 1] .= $this->_ins_suffix; |
437 | return $this->_lines($lines, ' ', false); |
438 | } |
439 | |
440 | function _deleted($lines, $words = false) |
441 | { |
442 | array_walk($lines, array(&$this, '_encode')); |
443 | $lines[0] = $this->_del_prefix . $lines[0]; |
444 | $lines[count($lines) - 1] .= $this->_del_suffix; |
445 | return $this->_lines($lines, ' ', false); |
446 | } |
447 | |
448 | function _changed($orig, $final) |
449 | { |
450 | // If we've already split on words, don't try to do so again - just display. |
451 | if ($this->_split_level == 'words') |
452 | { |
453 | $prefix = ''; |
454 | while ($orig[0] !== false && $final[0] !== false && substr($orig[0], 0, 1) == ' ' && substr($final[0], 0, 1) == ' ') |
455 | { |
456 | $prefix .= substr($orig[0], 0, 1); |
457 | $orig[0] = substr($orig[0], 1); |
458 | $final[0] = substr($final[0], 1); |
459 | } |
460 | |
461 | return $prefix . $this->_deleted($orig) . $this->_added($final); |
462 | } |
463 | |
464 | $text1 = implode("\n", $orig); |
465 | $text2 = implode("\n", $final); |
466 | |
467 | // Non-printing newline marker. |
468 | $nl = "\0"; |
469 | |
470 | // We want to split on word boundaries, but we need to preserve whitespace as well. |
471 | // Therefore we split on words, but include all blocks of whitespace in the wordlist. |
472 | $splitted_text_1 = $this->_split_on_words($text1, $nl); |
473 | $splitted_text_2 = $this->_split_on_words($text2, $nl); |
474 | |
475 | $diff = new diff($splitted_text_1, $splitted_text_2); |
476 | unset($splitted_text_1, $splitted_text_2); |
477 | |
478 | // Get the diff in inline format. |
479 | $renderer = new diff_renderer_inline(array_merge($this->get_params(), array('split_level' => 'words'))); |
480 | |
481 | // Run the diff and get the output. |
482 | return str_replace($nl, "\n", $renderer->render($diff)) . "\n"; |
483 | } |
484 | |
485 | function _split_on_words($string, $newline_escape = "\n") |
486 | { |
487 | // Ignore \0; otherwise the while loop will never finish. |
488 | $string = str_replace("\0", '', $string); |
489 | |
490 | $words = array(); |
491 | $length = strlen($string); |
492 | $pos = 0; |
493 | |
494 | $tab_there = true; |
495 | while ($pos < $length) |
496 | { |
497 | // Check for tabs... do not include them |
498 | if ($tab_there && substr($string, $pos, 1) === "\t") |
499 | { |
500 | $words[] = "\t"; |
501 | $pos++; |
502 | |
503 | continue; |
504 | } |
505 | else |
506 | { |
507 | $tab_there = false; |
508 | } |
509 | |
510 | // Eat a word with any preceding whitespace. |
511 | $spaces = strspn(substr($string, $pos), " \n"); |
512 | $nextpos = strcspn(substr($string, $pos + $spaces), " \n"); |
513 | $words[] = str_replace("\n", $newline_escape, substr($string, $pos, $spaces + $nextpos)); |
514 | $pos += $spaces + $nextpos; |
515 | } |
516 | |
517 | return $words; |
518 | } |
519 | |
520 | function _encode(&$string) |
521 | { |
522 | $string = htmlspecialchars($string, ENT_COMPAT); |
523 | } |
524 | } |
525 | |
526 | /** |
527 | * "raw" diff renderer. |
528 | * This class could be used to output a raw unified patch file |
529 | * |
530 | * @package diff |
531 | */ |
532 | class diff_renderer_raw extends diff_renderer |
533 | { |
534 | var $_leading_context_lines = 4; |
535 | var $_trailing_context_lines = 4; |
536 | |
537 | /** |
538 | * Our function to get the diff |
539 | */ |
540 | function get_diff_content($diff) |
541 | { |
542 | return '<textarea style="height: 290px;" rows="15" cols="76" class="full">' . htmlspecialchars($this->render($diff), ENT_COMPAT) . '</textarea>'; |
543 | } |
544 | |
545 | function _block_header($xbeg, $xlen, $ybeg, $ylen) |
546 | { |
547 | if ($xlen != 1) |
548 | { |
549 | $xbeg .= ',' . $xlen; |
550 | } |
551 | |
552 | if ($ylen != 1) |
553 | { |
554 | $ybeg .= ',' . $ylen; |
555 | } |
556 | return '@@ -' . $xbeg . ' +' . $ybeg . ' @@'; |
557 | } |
558 | |
559 | function _context($lines) |
560 | { |
561 | return $this->_lines($lines, ' '); |
562 | } |
563 | |
564 | function _added($lines) |
565 | { |
566 | return $this->_lines($lines, '+'); |
567 | } |
568 | |
569 | function _deleted($lines) |
570 | { |
571 | return $this->_lines($lines, '-'); |
572 | } |
573 | |
574 | function _changed($orig, $final) |
575 | { |
576 | return $this->_deleted($orig) . $this->_added($final); |
577 | } |
578 | } |
579 | |
580 | /** |
581 | * "chora (Horde)" diff renderer - similar style. |
582 | * This renderer class is a modified human_readable function from the Horde Framework. |
583 | * |
584 | * @package diff |
585 | */ |
586 | class diff_renderer_side_by_side extends diff_renderer |
587 | { |
588 | var $_leading_context_lines = 3; |
589 | var $_trailing_context_lines = 3; |
590 | |
591 | var $lines = array(); |
592 | |
593 | // Hold the left and right columns of lines for change blocks. |
594 | var $cols; |
595 | var $state; |
596 | |
597 | var $data = false; |
598 | |
599 | /** |
600 | * Our function to get the diff |
601 | */ |
602 | function get_diff_content($diff) |
603 | { |
604 | global $user; |
605 | |
606 | $output = ''; |
607 | $output .= '<table cellspacing="0" class="hrdiff"> |
608 | <caption> |
609 | <span class="unmodified"> </span> ' . $user->lang['LINE_UNMODIFIED'] . ' |
610 | <span class="added"> </span> ' . $user->lang['LINE_ADDED'] . ' |
611 | <span class="modified"> </span> ' . $user->lang['LINE_MODIFIED'] . ' |
612 | <span class="removed"> </span> ' . $user->lang['LINE_REMOVED'] . ' |
613 | </caption> |
614 | <tbody> |
615 | '; |
616 | |
617 | $this->render($diff); |
618 | |
619 | // Is the diff empty? |
620 | if (!count($this->lines)) |
621 | { |
622 | $output .= '<tr><th colspan="2">' . $user->lang['NO_VISIBLE_CHANGES'] . '</th></tr>'; |
623 | } |
624 | else |
625 | { |
626 | // Iterate through every header block of changes |
627 | foreach ($this->lines as $header) |
628 | { |
629 | $output .= '<tr><th>' . $user->lang['LINE'] . ' ' . $header['oldline'] . '</th><th>' . $user->lang['LINE'] . ' ' . $header['newline'] . '</th></tr>'; |
630 | |
631 | // Each header block consists of a number of changes (add, remove, change). |
632 | $current_context = ''; |
633 | |
634 | foreach ($header['contents'] as $change) |
635 | { |
636 | if (!empty($current_context) && $change['type'] != 'empty') |
637 | { |
638 | $line = $current_context; |
639 | $current_context = ''; |
640 | |
641 | $output .= '<tr class="unmodified"><td><pre>' . ((strlen($line)) ? $line : ' ') . '<br /></pre></td> |
642 | <td><pre>' . ((strlen($line)) ? $line : ' ') . '<br /></pre></td></tr>'; |
643 | } |
644 | |
645 | switch ($change['type']) |
646 | { |
647 | case 'add': |
648 | $line = ''; |
649 | |
650 | foreach ($change['lines'] as $_line) |
651 | { |
652 | $line .= htmlspecialchars($_line, ENT_COMPAT) . '<br />'; |
653 | } |
654 | |
655 | $output .= '<tr><td class="added_empty"> </td><td class="added"><pre>' . ((strlen($line)) ? $line : ' ') . '<br /></pre></td></tr>'; |
656 | break; |
657 | |
658 | case 'remove': |
659 | $line = ''; |
660 | |
661 | foreach ($change['lines'] as $_line) |
662 | { |
663 | $line .= htmlspecialchars($_line, ENT_COMPAT) . '<br />'; |
664 | } |
665 | |
666 | $output .= '<tr><td class="removed"><pre>' . ((strlen($line)) ? $line : ' ') . '<br /></pre></td><td class="removed_empty"> </td></tr>'; |
667 | break; |
668 | |
669 | case 'empty': |
670 | $current_context .= htmlspecialchars($change['line'], ENT_COMPAT) . '<br />'; |
671 | break; |
672 | |
673 | case 'change': |
674 | // Pop the old/new stacks one by one, until both are empty. |
675 | $oldsize = count($change['old']); |
676 | $newsize = count($change['new']); |
677 | $left = $right = ''; |
678 | |
679 | for ($row = 0, $row_max = max($oldsize, $newsize); $row < $row_max; ++$row) |
680 | { |
681 | $left .= isset($change['old'][$row]) ? htmlspecialchars($change['old'][$row], ENT_COMPAT) : ''; |
682 | $left .= '<br />'; |
683 | $right .= isset($change['new'][$row]) ? htmlspecialchars($change['new'][$row], ENT_COMPAT) : ''; |
684 | $right .= '<br />'; |
685 | } |
686 | |
687 | $output .= '<tr>'; |
688 | |
689 | if (!empty($left)) |
690 | { |
691 | $output .= '<td class="modified"><pre>' . $left . '<br /></pre></td>'; |
692 | } |
693 | else if ($row < $oldsize) |
694 | { |
695 | $output .= '<td class="modified"> </td>'; |
696 | } |
697 | else |
698 | { |
699 | $output .= '<td class="unmodified"> </td>'; |
700 | } |
701 | |
702 | if (!empty($right)) |
703 | { |
704 | $output .= '<td class="modified"><pre>' . $right . '<br /></pre></td>'; |
705 | } |
706 | else if ($row < $newsize) |
707 | { |
708 | $output .= '<td class="modified"> </td>'; |
709 | } |
710 | else |
711 | { |
712 | $output .= '<td class="unmodified"> </td>'; |
713 | } |
714 | |
715 | $output .= '</tr>'; |
716 | break; |
717 | } |
718 | } |
719 | |
720 | if (!empty($current_context)) |
721 | { |
722 | $line = $current_context; |
723 | $current_context = ''; |
724 | |
725 | $output .= '<tr class="unmodified"><td><pre>' . ((strlen($line)) ? $line : ' ') . '<br /></pre></td>'; |
726 | $output .= '<td><pre>' . ((strlen($line)) ? $line : ' ') . '<br /></pre></td></tr>'; |
727 | } |
728 | } |
729 | } |
730 | |
731 | $output .= '</tbody></table>'; |
732 | |
733 | return $output; |
734 | } |
735 | |
736 | function _start_diff() |
737 | { |
738 | $this->lines = array(); |
739 | |
740 | $this->data = false; |
741 | $this->cols = array(array(), array()); |
742 | $this->state = 'empty'; |
743 | |
744 | return ''; |
745 | } |
746 | |
747 | function _end_diff() |
748 | { |
749 | // Just flush any remaining entries in the columns stack. |
750 | switch ($this->state) |
751 | { |
752 | case 'add': |
753 | $this->data['contents'][] = array('type' => 'add', 'lines' => $this->cols[0]); |
754 | break; |
755 | |
756 | case 'remove': |
757 | // We have some removal lines pending in our stack, so flush them. |
758 | $this->data['contents'][] = array('type' => 'remove', 'lines' => $this->cols[0]); |
759 | break; |
760 | |
761 | case 'change': |
762 | // We have both remove and addition lines, so this is a change block. |
763 | $this->data['contents'][] = array('type' => 'change', 'old' => $this->cols[0], 'new' => $this->cols[1]); |
764 | break; |
765 | } |
766 | |
767 | if ($this->data !== false) |
768 | { |
769 | $this->lines[] = $this->data; |
770 | } |
771 | |
772 | return ''; |
773 | } |
774 | |
775 | function _block_header($xbeg, $xlen, $ybeg, $ylen) |
776 | { |
777 | // Push any previous header information to the return stack. |
778 | if ($this->data !== false) |
779 | { |
780 | $this->lines[] = $this->data; |
781 | } |
782 | |
783 | $this->data = array('type' => 'header', 'oldline' => $xbeg, 'newline' => $ybeg, 'contents' => array()); |
784 | $this->state = 'dump'; |
785 | } |
786 | |
787 | function _added($lines) |
788 | { |
789 | array_walk($lines, array(&$this, '_perform_add')); |
790 | } |
791 | |
792 | function _perform_add($line) |
793 | { |
794 | if ($this->state == 'empty') |
795 | { |
796 | return; |
797 | } |
798 | |
799 | // This is just an addition line. |
800 | if ($this->state == 'dump' || $this->state == 'add') |
801 | { |
802 | // Start adding to the addition stack. |
803 | $this->cols[0][] = $line; |
804 | $this->state = 'add'; |
805 | } |
806 | else |
807 | { |
808 | // This is inside a change block, so start accumulating lines. |
809 | $this->state = 'change'; |
810 | $this->cols[1][] = $line; |
811 | } |
812 | } |
813 | |
814 | function _deleted($lines) |
815 | { |
816 | array_walk($lines, array(&$this, '_perform_delete')); |
817 | } |
818 | |
819 | function _perform_delete($line) |
820 | { |
821 | // This is a removal line. |
822 | $this->state = 'remove'; |
823 | $this->cols[0][] = $line; |
824 | } |
825 | |
826 | function _context($lines) |
827 | { |
828 | array_walk($lines, array(&$this, '_perform_context')); |
829 | } |
830 | |
831 | function _perform_context($line) |
832 | { |
833 | // An empty block with no action. |
834 | switch ($this->state) |
835 | { |
836 | case 'add': |
837 | $this->data['contents'][] = array('type' => 'add', 'lines' => $this->cols[0]); |
838 | break; |
839 | |
840 | case 'remove': |
841 | // We have some removal lines pending in our stack, so flush them. |
842 | $this->data['contents'][] = array('type' => 'remove', 'lines' => $this->cols[0]); |
843 | break; |
844 | |
845 | case 'change': |
846 | // We have both remove and addition lines, so this is a change block. |
847 | $this->data['contents'][] = array('type' => 'change', 'old' => $this->cols[0], 'new' => $this->cols[1]); |
848 | break; |
849 | } |
850 | |
851 | $this->cols = array(array(), array()); |
852 | $this->data['contents'][] = array('type' => 'empty', 'line' => $line); |
853 | $this->state = 'dump'; |
854 | } |
855 | |
856 | function _changed($orig, $final) |
857 | { |
858 | return $this->_deleted($orig) . $this->_added($final); |
859 | } |
860 | |
861 | } |