1<?php 2// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project 3// 4// All Rights Reserved. See copyright.txt for details and a complete list of authors. 5// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details. 6// $Id$ 7 8/** 9 * HTML diff renderer. 10 * This class renders the diff of an HTML page with best effort. 11 */ 12 13include_once("Renderer.php"); 14 15class Text_Diff_Renderer_htmldiff extends Tiki_Text_Diff_Renderer 16{ 17 public function __construct($context_lines = 0, $words = 0) 18 { 19 $this->_leading_context_lines = $context_lines; 20 $this->_trailing_context_lines = $context_lines; 21 $this->_words = $words; 22 } 23 24 protected function _startDiff() 25 { 26 ob_start(); 27 $this->original = []; 28 $this->final = []; 29 $this->n = 0; 30 $this->rspan = false; 31 $this->lspan = false; 32 //$this->tracked_tags = array ("table","ul","div"); 33 $this->tracked_tags = ["table", "ul"]; 34 } 35 36 protected function _endDiff() 37 { 38 for ($i = 0; $i <= $this->n; $i++) { 39 if ($this->original[$i] != "" and $this->final[$i] != "") { 40 echo "<tr><td width='50%' colspan='2' style='vertical-align:top'>" . $this->original[$i] . "</td><td width='50%' colspan='2' style='vertical-align:top'>" . $this->final[$i] . "</td></tr>\n"; 41 } 42 } 43 //echo '</table>'; 44 $val = ob_get_contents(); 45 ob_end_clean(); 46 return $val; 47 } 48 49 protected function _blockHeader($xbeg, $xlen, $ybeg, $ylen) 50 { 51 return "$xbeg,$xlen,$ybeg,$ylen"; 52 } 53 54 protected function _startBlock($header) 55 { 56 } 57 58 protected function _endBlock() 59 { 60 } 61 62 protected function _insert_tag($line, $tag, &$span) 63 { 64 $string = ""; 65 if ($line != '') { 66 if (strstr($line, "<") === false) { 67 if ($span === false) { 68 $string .= "<span class='$tag'>"; 69 $span = true; 70 } 71 $string .= $line; 72 } else { 73 if ($span === true) { 74 $string .= "</span class='fin'>"; 75 $span = false; 76 } 77 if (strstr($line, "class=") === false) { 78 $string .= preg_replace("#<([^/> ]+)(.*[^/]?)?>#", "<$1 class='$tag' $2>", $line); 79 $string = preg_replace("#<br class='(.*)'\s*/>#", "<span class='$1'>↵</span><br class='$1' />", $string); 80 } else { 81 $string .= preg_replace("#<([^/> ]+)(.*)class=[\"']?([^\"']+)[\"']?(.*[^/]?)?>#", "<$1$2 class='$3 $tag' $4>", $line); 82 } 83 } 84 } 85 return $string; 86 } 87 88 protected function _count_tags($line, $version) 89 { 90 91 preg_match("#<(/?)([^ >]+)#", $line, $out); 92 if (count($out) > 1 && in_array($out[2], $this->tracked_tags)) { 93 if (isset($this->tags[$version][$out[2]])) { 94 if ($out[1] == '/') { 95 $this->tags[$version][$out[2]]--; 96 } else { 97 $this->tags[$version][$out[2]]++; 98 } 99 } 100 } 101 } 102 103 protected function _can_break($line) 104 { 105 106 if (preg_match("#<(p|h\d|br)#", $line) == 0) { 107 return false; 108 } 109 110 if (isset($this->tags)) { 111 foreach ($this->tags as $v) { 112 foreach ($v as $tag) { 113 if ($tag != 0) { 114 return false; 115 } 116 } 117 } 118 } 119 return true; 120 } 121 122 protected function _lines($lines, $prefix = '', $suffix = '', $type = '') 123 { 124 static $context = 0; 125 126 switch ($type) { 127 case 'context': 128 foreach ($lines as $line) { 129 if ($context == 0 and $this->_can_break($line)) { 130 $context = 1; 131 $this->n++; 132 } 133 134 $this->_count_tags($line, 'original'); 135 $this->_count_tags($line, 'final'); 136 if ($this->lspan === true) { 137 $this->original[$this->n] .= "</span>"; 138 $this->lspan = false; 139 } 140 if ($this->rspan === true) { 141 $this->final[$this->n] .= "</span>"; 142 $this->rspan = false; 143 } 144 if (! isset($this->original[$this->n])) { 145 $this->original[$this->n] = ''; 146 } 147 $this->original[$this->n] .= "$line"; 148 if (! isset($this->final[$this->n])) { 149 $this->final[$this->n] = ''; 150 } 151 $this->final[$this->n] .= "$line"; 152 } 153 break; 154 case 'change-added': 155 case 'added': 156 foreach ($lines as $line) { 157 if ($line != '') { 158 $this->_count_tags($line, 'final'); 159 $this->final[$this->n] .= $this->_insert_tag($line, 'diffadded', $this->rspan); 160 $context = 0; 161 } 162 } 163 break; 164 case 'deleted': 165 case 'change-deleted': 166 foreach ($lines as $line) { 167 if ($line != '') { 168 $this->_count_tags($line, 'original'); 169 $this->original[$this->n] .= $this->_insert_tag($line, 'diffdeleted', $this->lspan); 170 $context = 0; 171 } 172 } 173 break; 174 } 175 } 176 177 protected function _context($lines) 178 { 179 $this->_lines($lines, '', '', 'context'); 180 } 181 182 protected function _added($lines, $changemode = false) 183 { 184 if ($changemode) { 185 $this->_lines($lines, '+', '', 'change-added'); 186 } else { 187 $this->_lines($lines, '+', '', 'added'); 188 } 189 } 190 191 protected function _deleted($lines, $changemode = false) 192 { 193 if ($changemode) { 194 $this->_lines($lines, '-', '', 'change-deleted'); 195 } else { 196 $this->_lines($lines, '-', '', 'deleted'); 197 } 198 } 199 200 protected function _changed($orig, $final) 201 { 202 $this->_deleted($orig, true); 203 $this->_added($final, true); 204 } 205} 206