Index: trunk/phase3/includes/AutoLoader.php |
— | — | @@ -376,13 +376,7 @@ |
377 | 377 | 'IBM_DB2Field' => 'includes/db/DatabaseIbm_db2.php', |
378 | 378 | |
379 | 379 | # includes/diff |
380 | | - 'AncestorComparator' => 'includes/diff/HTMLDiff.php', |
381 | | - 'AnchorToString' => 'includes/diff/HTMLDiff.php', |
382 | 380 | 'ArrayDiffFormatter' => 'includes/diff/DifferenceEngine.php', |
383 | | - 'BodyNode' => 'includes/diff/Nodes.php', |
384 | | - 'ChangeText' => 'includes/diff/HTMLDiff.php', |
385 | | - 'ChangeTextGenerator' => 'includes/diff/HTMLDiff.php', |
386 | | - 'DelegatingContentHandler' => 'includes/diff/HTMLDiff.php', |
387 | 381 | '_DiffEngine' => 'includes/diff/DifferenceEngine.php', |
388 | 382 | 'DifferenceEngine' => 'includes/diff/DifferenceInterface.php', |
389 | 383 | 'DiffFormatter' => 'includes/diff/DifferenceEngine.php', |
— | — | @@ -392,27 +386,11 @@ |
393 | 387 | '_DiffOp_Copy' => 'includes/diff/DifferenceEngine.php', |
394 | 388 | '_DiffOp_Delete' => 'includes/diff/DifferenceEngine.php', |
395 | 389 | '_DiffOp' => 'includes/diff/DifferenceEngine.php', |
396 | | - 'DomTreeBuilder' => 'includes/diff/HTMLDiff.php', |
397 | | - 'DummyNode' => 'includes/diff/Nodes.php', |
398 | | - 'HTMLDiffer' => 'includes/diff/HTMLDiff.php', |
399 | | - 'HTMLOutput' => 'includes/diff/HTMLDiff.php', |
400 | 390 | '_HWLDF_WordAccumulator' => 'includes/diff/DifferenceEngine.php', |
401 | | - 'ImageNode' => 'includes/diff/Nodes.php', |
402 | | - 'LastCommonParentResult' => 'includes/diff/HTMLDiff.php', |
403 | 391 | 'MappedDiff' => 'includes/diff/DifferenceEngine.php', |
404 | | - 'Modification' => 'includes/diff/HTMLDiff.php', |
405 | | - 'NoContentTagToString' => 'includes/diff/HTMLDiff.php', |
406 | | - 'Node' => 'includes/diff/Nodes.php', |
407 | 392 | 'RangeDifference' => 'includes/diff/Diff.php', |
408 | 393 | 'TableDiffFormatter' => 'includes/diff/DifferenceEngine.php', |
409 | | - 'TagNode' => 'includes/diff/Nodes.php', |
410 | | - 'TagToString' => 'includes/diff/HTMLDiff.php', |
411 | | - 'TagToStringFactory' => 'includes/diff/HTMLDiff.php', |
412 | | - 'TextNode' => 'includes/diff/Nodes.php', |
413 | | - 'TextNodeDiffer' => 'includes/diff/HTMLDiff.php', |
414 | | - 'TextOnlyComparator' => 'includes/diff/HTMLDiff.php', |
415 | 394 | 'UnifiedDiffFormatter' => 'includes/diff/DifferenceEngine.php', |
416 | | - 'WhiteSpaceNode' => 'includes/diff/Nodes.php', |
417 | 395 | 'WikiDiff3' => 'includes/diff/Diff.php', |
418 | 396 | 'WordLevelDiff' => 'includes/diff/DifferenceEngine.php', |
419 | 397 | |
Index: trunk/phase3/includes/DefaultSettings.php |
— | — | @@ -2892,9 +2892,6 @@ |
2893 | 2893 | /** Name of the external diff engine to use */ |
2894 | 2894 | $wgExternalDiffEngine = false; |
2895 | 2895 | |
2896 | | -/** Whether to use inline diff */ |
2897 | | -$wgEnableHtmlDiff = false; |
2898 | | - |
2899 | 2896 | /** Use RC Patrolling to check for vandalism */ |
2900 | 2897 | $wgUseRCPatrol = true; |
2901 | 2898 | |
Index: trunk/phase3/includes/diff/Nodes.php |
— | — | @@ -1,439 +0,0 @@ |
2 | | -<?php |
3 | | - |
4 | | -/** Copyright (C) 2008 Guy Van den Broeck <guy@guyvdb.eu> |
5 | | - * |
6 | | - * This program is free software; you can redistribute it and/or modify |
7 | | - * it under the terms of the GNU General Public License as published by |
8 | | - * the Free Software Foundation; either version 2 of the License, or |
9 | | - * (at your option) any later version. |
10 | | - * |
11 | | - * This program is distributed in the hope that it will be useful, |
12 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | - * GNU General Public License for more details. |
15 | | - * |
16 | | - * You should have received a copy of the GNU General Public License |
17 | | - * along with this program; if not, write to the Free Software |
18 | | - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
19 | | - * or see http://www.gnu.org/ |
20 | | - * |
21 | | - */ |
22 | | - |
23 | | -/** |
24 | | - * Any element in the DOM tree of an HTML document. |
25 | | - * @ingroup DifferenceEngine |
26 | | - */ |
27 | | -class Node { |
28 | | - |
29 | | - public $parent; |
30 | | - |
31 | | - protected $parentTree; |
32 | | - |
33 | | - public $whiteBefore = false; |
34 | | - |
35 | | - public $whiteAfter = false; |
36 | | - |
37 | | - function __construct($parent) { |
38 | | - $this->parent = $parent; |
39 | | - } |
40 | | - |
41 | | - public function getParentTree() { |
42 | | - if (!isset($this->parentTree)) { |
43 | | - if (!is_null($this->parent)) { |
44 | | - $this->parentTree = $this->parent->getParentTree(); |
45 | | - $this->parentTree[] = $this->parent; |
46 | | - } else { |
47 | | - $this->parentTree = array(); |
48 | | - } |
49 | | - } |
50 | | - return $this->parentTree; |
51 | | - } |
52 | | - |
53 | | - public function getLastCommonParent(Node $other) { |
54 | | - $result = new LastCommonParentResult(); |
55 | | - |
56 | | - $myParents = $this->getParentTree(); |
57 | | - $otherParents = $other->getParentTree(); |
58 | | - |
59 | | - $i = 1; |
60 | | - $isSame = true; |
61 | | - $nbMyParents = count($myParents); |
62 | | - $nbOtherParents = count($otherParents); |
63 | | - while ($isSame && $i < $nbMyParents && $i < $nbOtherParents) { |
64 | | - if (!$myParents[$i]->openingTag === $otherParents[$i]->openingTag) { |
65 | | - $isSame = false; |
66 | | - } else { |
67 | | - // After a while, the index i-1 must be the last common parent |
68 | | - $i++; |
69 | | - } |
70 | | - } |
71 | | - |
72 | | - $result->lastCommonParentDepth = $i - 1; |
73 | | - $result->parent = $myParents[$i - 1]; |
74 | | - |
75 | | - if (!$isSame || $nbMyParents > $nbOtherParents) { |
76 | | - // Not all tags matched, or all tags matched but |
77 | | - // there are tags left in this tree |
78 | | - $result->indexInLastCommonParent = $myParents[$i - 1]->getIndexOf($myParents[$i]); |
79 | | - $result->splittingNeeded = true; |
80 | | - } else if ($nbMyParents <= $nbOtherParents) { |
81 | | - $result->indexInLastCommonParent = $myParents[$i - 1]->getIndexOf($this); |
82 | | - } |
83 | | - return $result; |
84 | | - } |
85 | | - |
86 | | - public function setParent($parent) { |
87 | | - $this->parent = $parent; |
88 | | - unset($this->parentTree); |
89 | | - } |
90 | | - |
91 | | - public function inPre() { |
92 | | - $tree = $this->getParentTree(); |
93 | | - foreach ($tree as &$ancestor) { |
94 | | - if ($ancestor->isPre()) { |
95 | | - return true; |
96 | | - } |
97 | | - } |
98 | | - return false; |
99 | | - } |
100 | | -} |
101 | | - |
102 | | -/** |
103 | | - * Node that can contain other nodes. Represents an HTML tag. |
104 | | - * @ingroup DifferenceEngine |
105 | | - */ |
106 | | -class TagNode extends Node { |
107 | | - |
108 | | - public $children = array(); |
109 | | - |
110 | | - public $qName; |
111 | | - |
112 | | - public $attributes = array(); |
113 | | - |
114 | | - public $openingTag; |
115 | | - |
116 | | - function __construct($parent, $qName, /*array*/ $attributes) { |
117 | | - parent::__construct($parent); |
118 | | - $this->qName = strtolower($qName); |
119 | | - foreach($attributes as $key => &$value){ |
120 | | - $this->attributes[strtolower($key)] = $value; |
121 | | - } |
122 | | - return $this->openingTag = Xml::openElement($this->qName, $this->attributes); |
123 | | - } |
124 | | - |
125 | | - public function addChildAbsolute(Node $node, $index) { |
126 | | - array_splice($this->children, $index, 0, array($node)); |
127 | | - } |
128 | | - |
129 | | - public function getIndexOf(Node $child) { |
130 | | - // don't trust array_search with objects |
131 | | - foreach ($this->children as $key => &$value){ |
132 | | - if ($value === $child) { |
133 | | - return $key; |
134 | | - } |
135 | | - } |
136 | | - return null; |
137 | | - } |
138 | | - |
139 | | - public function getNbChildren() { |
140 | | - return count($this->children); |
141 | | - } |
142 | | - |
143 | | - public function getMinimalDeletedSet($id, &$allDeleted, &$somethingDeleted) { |
144 | | - $nodes = array(); |
145 | | - |
146 | | - $allDeleted = false; |
147 | | - $somethingDeleted = false; |
148 | | - $hasNonDeletedDescendant = false; |
149 | | - |
150 | | - if (empty($this->children)) { |
151 | | - return $nodes; |
152 | | - } |
153 | | - |
154 | | - foreach ($this->children as &$child) { |
155 | | - $allDeleted_local = false; |
156 | | - $somethingDeleted_local = false; |
157 | | - $childrenChildren = $child->getMinimalDeletedSet($id, $allDeleted_local, $somethingDeleted_local); |
158 | | - if ($somethingDeleted_local) { |
159 | | - $nodes = array_merge($nodes, $childrenChildren); |
160 | | - $somethingDeleted = true; |
161 | | - } |
162 | | - if (!$allDeleted_local) { |
163 | | - $hasNonDeletedDescendant = true; |
164 | | - } |
165 | | - } |
166 | | - if (!$hasNonDeletedDescendant) { |
167 | | - $nodes = array($this); |
168 | | - $allDeleted = true; |
169 | | - } |
170 | | - return $nodes; |
171 | | - } |
172 | | - |
173 | | - public function splitUntil(TagNode $parent, Node $split, $includeLeft) { |
174 | | - $splitOccured = false; |
175 | | - if ($parent !== $this) { |
176 | | - $part1 = new TagNode(null, $this->qName, $this->attributes); |
177 | | - $part2 = new TagNode(null, $this->qName, $this->attributes); |
178 | | - $part1->setParent($this->parent); |
179 | | - $part2->setParent($this->parent); |
180 | | - |
181 | | - $onSplit = false; |
182 | | - $pastSplit = false; |
183 | | - foreach ($this->children as &$child) |
184 | | - { |
185 | | - if ($child === $split) { |
186 | | - $onSplit = true; |
187 | | - } |
188 | | - if(!$pastSplit || ($onSplit && $includeLeft)) { |
189 | | - $child->setParent($part1); |
190 | | - $part1->children[] = $child; |
191 | | - } else { |
192 | | - $child->setParent($part2); |
193 | | - $part2->children[] = $child; |
194 | | - } |
195 | | - if ($onSplit) { |
196 | | - $onSplit = false; |
197 | | - $pastSplit = true; |
198 | | - } |
199 | | - } |
200 | | - $myindexinparent = $this->parent->getIndexOf($this); |
201 | | - if (!empty($part1->children)) { |
202 | | - $this->parent->addChildAbsolute($part1, $myindexinparent); |
203 | | - } |
204 | | - if (!empty($part2->children)) { |
205 | | - $this->parent->addChildAbsolute($part2, $myindexinparent); |
206 | | - } |
207 | | - if (!empty($part1->children) && !empty($part2->children)) { |
208 | | - $splitOccured = true; |
209 | | - } |
210 | | - |
211 | | - $this->parent->removeChild($myindexinparent); |
212 | | - |
213 | | - if ($includeLeft) { |
214 | | - $this->parent->splitUntil($parent, $part1, $includeLeft); |
215 | | - } else { |
216 | | - $this->parent->splitUntil($parent, $part2, $includeLeft); |
217 | | - } |
218 | | - } |
219 | | - return $splitOccured; |
220 | | - |
221 | | - } |
222 | | - |
223 | | - private function removeChild($index) { |
224 | | - unset($this->children[$index]); |
225 | | - $this->children = array_values($this->children); |
226 | | - } |
227 | | - |
228 | | - public static $blocks = array('html', 'body','p','blockquote', 'h1', |
229 | | - 'h2', 'h3', 'h4', 'h5', 'pre', 'div', 'ul', 'ol', 'li', 'table', |
230 | | - 'tbody', 'tr', 'td', 'th', 'br'); |
231 | | - |
232 | | - public function copyTree() { |
233 | | - $newThis = new TagNode(null, $this->qName, $this->attributes); |
234 | | - $newThis->whiteBefore = $this->whiteBefore; |
235 | | - $newThis->whiteAfter = $this->whiteAfter; |
236 | | - foreach ($this->children as &$child) { |
237 | | - $newChild = $child->copyTree(); |
238 | | - $newChild->setParent($newThis); |
239 | | - $newThis->children[] = $newChild; |
240 | | - } |
241 | | - return $newThis; |
242 | | - } |
243 | | - |
244 | | - public function getMatchRatio(TagNode $other) { |
245 | | - $txtComp = new TextOnlyComparator($other); |
246 | | - return $txtComp->getMatchRatio(new TextOnlyComparator($this)); |
247 | | - } |
248 | | - |
249 | | - public function expandWhiteSpace() { |
250 | | - $shift = 0; |
251 | | - $spaceAdded = false; |
252 | | - |
253 | | - $nbOriginalChildren = $this->getNbChildren(); |
254 | | - for ($i = 0; $i < $nbOriginalChildren; ++$i) { |
255 | | - $child = $this->children[$i + $shift]; |
256 | | - |
257 | | - if ($child instanceof TagNode) { |
258 | | - if (!$child->isPre()) { |
259 | | - $child->expandWhiteSpace(); |
260 | | - } |
261 | | - } |
262 | | - if (!$spaceAdded && $child->whiteBefore) { |
263 | | - $ws = new WhiteSpaceNode(null, ' ', $child->getLeftMostChild()); |
264 | | - $ws->setParent($this); |
265 | | - $this->addChildAbsolute($ws,$i + ($shift++)); |
266 | | - } |
267 | | - if ($child->whiteAfter) { |
268 | | - $ws = new WhiteSpaceNode(null, ' ', $child->getRightMostChild()); |
269 | | - $ws->setParent($this); |
270 | | - $this->addChildAbsolute($ws,$i + 1 + ($shift++)); |
271 | | - $spaceAdded = true; |
272 | | - } else { |
273 | | - $spaceAdded = false; |
274 | | - } |
275 | | - |
276 | | - } |
277 | | - } |
278 | | - |
279 | | - public function getLeftMostChild() { |
280 | | - if (empty($this->children)) { |
281 | | - return $this; |
282 | | - } |
283 | | - return $this->children[0]->getLeftMostChild(); |
284 | | - } |
285 | | - |
286 | | - public function getRightMostChild() { |
287 | | - if (empty($this->children)) { |
288 | | - return $this; |
289 | | - } |
290 | | - return $this->children[$this->getNbChildren() - 1]->getRightMostChild(); |
291 | | - } |
292 | | - |
293 | | - public function isPre() { |
294 | | - return 0 == strcasecmp($this->qName,'pre'); |
295 | | - } |
296 | | - |
297 | | - public static function toDiffLine(TagNode $node) { |
298 | | - return $node->openingTag; |
299 | | - } |
300 | | -} |
301 | | - |
302 | | -/** |
303 | | - * Represents a piece of text in the HTML file. |
304 | | - * @ingroup DifferenceEngine |
305 | | - */ |
306 | | -class TextNode extends Node { |
307 | | - |
308 | | - public $text; |
309 | | - |
310 | | - public $modification; |
311 | | - |
312 | | - function __construct($parent, $text) { |
313 | | - parent::__construct($parent); |
314 | | - $this->modification = new Modification(Modification::NONE); |
315 | | - $this->text = $text; |
316 | | - } |
317 | | - |
318 | | - public function copyTree() { |
319 | | - $clone = clone $this; |
320 | | - $clone->setParent(null); |
321 | | - return $clone; |
322 | | - } |
323 | | - |
324 | | - public function getLeftMostChild() { |
325 | | - return $this; |
326 | | - } |
327 | | - |
328 | | - public function getRightMostChild() { |
329 | | - return $this; |
330 | | - } |
331 | | - |
332 | | - public function getMinimalDeletedSet($id, &$allDeleted, &$somethingDeleted) { |
333 | | - if ($this->modification->type == Modification::REMOVED |
334 | | - && $this->modification->id == $id){ |
335 | | - $somethingDeleted = true; |
336 | | - $allDeleted = true; |
337 | | - return array($this); |
338 | | - } |
339 | | - return array(); |
340 | | - } |
341 | | - |
342 | | - public function isSameText($other) { |
343 | | - if (is_null($other) || ! $other instanceof TextNode) { |
344 | | - return false; |
345 | | - } |
346 | | - return str_replace('\n', ' ',$this->text) === str_replace('\n', ' ',$other->text); |
347 | | - } |
348 | | - |
349 | | - public static function toDiffLine(TextNode $node) { |
350 | | - return str_replace('\n', ' ',$node->text); |
351 | | - } |
352 | | -} |
353 | | - |
354 | | -/** |
355 | | - * @todo Document |
356 | | - * @ingroup DifferenceEngine |
357 | | - */ |
358 | | -class WhiteSpaceNode extends TextNode { |
359 | | - |
360 | | - function __construct($parent, $s, Node $like = null) { |
361 | | - parent::__construct($parent, $s); |
362 | | - if(!is_null($like) && $like instanceof TextNode) { |
363 | | - $newModification = clone $like->modification; |
364 | | - $newModification->firstOfID = false; |
365 | | - $this->modification = $newModification; |
366 | | - } |
367 | | - } |
368 | | -} |
369 | | - |
370 | | -/** |
371 | | - * Represents the root of a HTML document. |
372 | | - * @ingroup DifferenceEngine |
373 | | - */ |
374 | | -class BodyNode extends TagNode { |
375 | | - |
376 | | - function __construct() { |
377 | | - parent::__construct(null, 'body', array()); |
378 | | - } |
379 | | - |
380 | | - public function copyTree() { |
381 | | - $newThis = new BodyNode(); |
382 | | - foreach ($this->children as &$child) { |
383 | | - $newChild = $child->copyTree(); |
384 | | - $newChild->setParent($newThis); |
385 | | - $newThis->children[] = $newChild; |
386 | | - } |
387 | | - return $newThis; |
388 | | - } |
389 | | - |
390 | | - public function getMinimalDeletedSet($id, &$allDeleted, &$somethingDeleted) { |
391 | | - $nodes = array(); |
392 | | - foreach ($this->children as &$child) { |
393 | | - $childrenChildren = $child->getMinimalDeletedSet($id, |
394 | | - $allDeleted, $somethingDeleted); |
395 | | - $nodes = array_merge($nodes, $childrenChildren); |
396 | | - } |
397 | | - return $nodes; |
398 | | - } |
399 | | - |
400 | | -} |
401 | | - |
402 | | -/** |
403 | | - * Represents an image in HTML. Even though images do not contain any text they |
404 | | - * are independent visible objects on the page. They are logically a TextNode. |
405 | | - * @ingroup DifferenceEngine |
406 | | - */ |
407 | | -class ImageNode extends TextNode { |
408 | | - |
409 | | - public $attributes; |
410 | | - |
411 | | - function __construct(TagNode $parent, /*array*/ $attrs) { |
412 | | - if(!array_key_exists('src', $attrs)) { |
413 | | - HTMLDiffer::diffDebug( "Image without a source\n" ); |
414 | | - parent::__construct($parent, '<img></img>'); |
415 | | - }else{ |
416 | | - parent::__construct($parent, '<img>' . strtolower($attrs['src']) . '</img>'); |
417 | | - } |
418 | | - $this->attributes = $attrs; |
419 | | - } |
420 | | - |
421 | | - public function isSameText($other) { |
422 | | - if (is_null($other) || ! $other instanceof ImageNode) { |
423 | | - return false; |
424 | | - } |
425 | | - return $this->text === $other->text; |
426 | | - } |
427 | | - |
428 | | -} |
429 | | - |
430 | | -/** |
431 | | - * No-op node |
432 | | - * @ingroup DifferenceEngine |
433 | | - */ |
434 | | -class DummyNode extends Node { |
435 | | - |
436 | | - function __construct() { |
437 | | - // no op |
438 | | - } |
439 | | - |
440 | | -} |
Index: trunk/phase3/includes/diff/HTMLDiff.php |
— | — | @@ -1,1009 +0,0 @@ |
2 | | -<?php |
3 | | - |
4 | | -/** Copyright (C) 2008 Guy Van den Broeck <guy@guyvdb.eu> |
5 | | - * |
6 | | - * This program is free software; you can redistribute it and/or modify |
7 | | - * it under the terms of the GNU General Public License as published by |
8 | | - * the Free Software Foundation; either version 2 of the License, or |
9 | | - * (at your option) any later version. |
10 | | - * |
11 | | - * This program is distributed in the hope that it will be useful, |
12 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | - * GNU General Public License for more details. |
15 | | - * |
16 | | - * You should have received a copy of the GNU General Public License |
17 | | - * along with this program; if not, write to the Free Software |
18 | | - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
19 | | - * or see http://www.gnu.org/ |
20 | | - * |
21 | | - * @ingroup DifferenceEngine |
22 | | - */ |
23 | | - |
24 | | -/** |
25 | | - * When detecting the last common parent of two nodes, all results are stored as |
26 | | - * a LastCommonParentResult. |
27 | | - */ |
28 | | -class LastCommonParentResult { |
29 | | - |
30 | | - // Parent |
31 | | - public $parent; |
32 | | - |
33 | | - // Splitting |
34 | | - public $splittingNeeded = false; |
35 | | - |
36 | | - // Depth |
37 | | - public $lastCommonParentDepth = -1; |
38 | | - |
39 | | - // Index |
40 | | - public $indexInLastCommonParent = -1; |
41 | | -} |
42 | | - |
43 | | -class Modification{ |
44 | | - |
45 | | - const NONE = 1; |
46 | | - const REMOVED = 2; |
47 | | - const ADDED = 4; |
48 | | - const CHANGED = 8; |
49 | | - |
50 | | - public $type; |
51 | | - |
52 | | - public $id = -1; |
53 | | - |
54 | | - public $firstOfID = false; |
55 | | - |
56 | | - public $changes; |
57 | | - |
58 | | - function __construct($type) { |
59 | | - $this->type = $type; |
60 | | - } |
61 | | - |
62 | | - public static function typeToString($type) { |
63 | | - switch($type) { |
64 | | - case self::NONE: return 'none'; |
65 | | - case self::REMOVED: return 'removed'; |
66 | | - case self::ADDED: return 'added'; |
67 | | - case self::CHANGED: return 'changed'; |
68 | | - } |
69 | | - } |
70 | | -} |
71 | | - |
72 | | -class DomTreeBuilder { |
73 | | - |
74 | | - public $textNodes = array(); |
75 | | - |
76 | | - public $bodyNode; |
77 | | - |
78 | | - private $currentParent; |
79 | | - |
80 | | - private $newWord = ''; |
81 | | - |
82 | | - protected $bodyStarted = false; |
83 | | - |
84 | | - protected $bodyEnded = false; |
85 | | - |
86 | | - private $whiteSpaceBeforeThis = false; |
87 | | - |
88 | | - private $lastSibling; |
89 | | - |
90 | | - private $notInPre = true; |
91 | | - |
92 | | - function __construct() { |
93 | | - $this->bodyNode = $this->currentParent = new BodyNode(); |
94 | | - $this->lastSibling = new DummyNode(); |
95 | | - } |
96 | | - |
97 | | - /** |
98 | | - * Must be called manually |
99 | | - */ |
100 | | - public function endDocument() { |
101 | | - $this->endWord(); |
102 | | - HTMLDiffer::diffDebug( count($this->textNodes) . " text nodes in document.\n" ); |
103 | | - } |
104 | | - |
105 | | - public function startElement($parser, $name, /*array*/ $attributes) { |
106 | | - if (strcasecmp($name, 'body') != 0) { |
107 | | - HTMLDiffer::diffDebug( "Starting $name node.\n" ); |
108 | | - $this->endWord(); |
109 | | - |
110 | | - $newNode = new TagNode($this->currentParent, $name, $attributes); |
111 | | - $this->currentParent->children[] = $newNode; |
112 | | - $this->currentParent = $newNode; |
113 | | - $this->lastSibling = new DummyNode(); |
114 | | - if ($this->whiteSpaceBeforeThis && !in_array(strtolower($this->currentParent->qName),TagNode::$blocks)) { |
115 | | - $this->currentParent->whiteBefore = true; |
116 | | - } |
117 | | - $this->whiteSpaceBeforeThis = false; |
118 | | - if(strcasecmp($name, 'pre') == 0) { |
119 | | - $this->notInPre = false; |
120 | | - } |
121 | | - } |
122 | | - } |
123 | | - |
124 | | - public function endElement($parser, $name) { |
125 | | - if(strcasecmp($name, 'body') != 0) { |
126 | | - HTMLDiffer::diffDebug( "Ending $name node.\n"); |
127 | | - if (0 == strcasecmp($name,'img')) { |
128 | | - // Insert a dummy leaf for the image |
129 | | - $img = new ImageNode($this->currentParent, $this->currentParent->attributes); |
130 | | - $this->currentParent->children[] = $img; |
131 | | - $img->whiteBefore = $this->whiteSpaceBeforeThis; |
132 | | - $this->lastSibling = $img; |
133 | | - $this->textNodes[] = $img; |
134 | | - } |
135 | | - $this->endWord(); |
136 | | - if (!in_array(strtolower($this->currentParent->qName),TagNode::$blocks)) { |
137 | | - $this->lastSibling = $this->currentParent; |
138 | | - } else { |
139 | | - $this->lastSibling = new DummyNode(); |
140 | | - } |
141 | | - $this->currentParent = $this->currentParent->parent; |
142 | | - $this->whiteSpaceBeforeThis = false; |
143 | | - if (!$this->notInPre && strcasecmp($name, 'pre') == 0) { |
144 | | - $this->notInPre = true; |
145 | | - } |
146 | | - } else { |
147 | | - $this->endDocument(); |
148 | | - } |
149 | | - } |
150 | | - |
151 | | - const regex = '/([\s\.\,\"\\\'\(\)\?\:\;\!\{\}\-\+\*\=\_\[\]\&\|\$]{1})/'; |
152 | | - const whitespace = '/^[\s]{1}$/'; |
153 | | - const delimiter = '/^[\s\.\,\"\\\'\(\)\?\:\;\!\{\}\-\+\*\=\_\[\]\&\|\$]{1}$/'; |
154 | | - |
155 | | - public function characters($parser, $data) { |
156 | | - $matches = preg_split(self::regex, $data, -1, PREG_SPLIT_DELIM_CAPTURE); |
157 | | - |
158 | | - foreach($matches as &$word) { |
159 | | - if (preg_match(self::whitespace, $word) && $this->notInPre) { |
160 | | - $this->endWord(); |
161 | | - $this->lastSibling->whiteAfter = true; |
162 | | - $this->whiteSpaceBeforeThis = true; |
163 | | - } else if (preg_match(self::delimiter, $word)) { |
164 | | - $this->endWord(); |
165 | | - $textNode = new TextNode($this->currentParent, $word); |
166 | | - $this->currentParent->children[] = $textNode; |
167 | | - $textNode->whiteBefore = $this->whiteSpaceBeforeThis; |
168 | | - $this->whiteSpaceBeforeThis = false; |
169 | | - $this->lastSibling = $textNode; |
170 | | - $this->textNodes[] = $textNode; |
171 | | - } else { |
172 | | - $this->newWord .= $word; |
173 | | - } |
174 | | - } |
175 | | - } |
176 | | - |
177 | | - private function endWord() { |
178 | | - if ($this->newWord !== '') { |
179 | | - $node = new TextNode($this->currentParent, $this->newWord); |
180 | | - $this->currentParent->children[] = $node; |
181 | | - $node->whiteBefore = $this->whiteSpaceBeforeThis; |
182 | | - $this->whiteSpaceBeforeThis = false; |
183 | | - $this->lastSibling = $node; |
184 | | - $this->textNodes[] = $node; |
185 | | - $this->newWord = ""; |
186 | | - } |
187 | | - } |
188 | | - |
189 | | - public function getDiffLines() { |
190 | | - return array_map(array('TextNode','toDiffLine'), $this->textNodes); |
191 | | - } |
192 | | -} |
193 | | - |
194 | | -class TextNodeDiffer { |
195 | | - |
196 | | - private $textNodes; |
197 | | - public $bodyNode; |
198 | | - |
199 | | - private $oldTextNodes; |
200 | | - private $oldBodyNode; |
201 | | - |
202 | | - private $newID = 0; |
203 | | - |
204 | | - private $changedID = 0; |
205 | | - |
206 | | - private $changedIDUsed = false; |
207 | | - |
208 | | - // used to remove the whitespace between a red and green block |
209 | | - private $whiteAfterLastChangedPart = false; |
210 | | - |
211 | | - private $deletedID = 0; |
212 | | - |
213 | | - function __construct(DomTreeBuilder $tree, DomTreeBuilder $oldTree) { |
214 | | - $this->textNodes = $tree->textNodes; |
215 | | - $this->bodyNode = $tree->bodyNode; |
216 | | - $this->oldTextNodes = $oldTree->textNodes; |
217 | | - $this->oldBodyNode = $oldTree->bodyNode; |
218 | | - } |
219 | | - |
220 | | - public function markAsNew($start, $end) { |
221 | | - if ($end <= $start) { |
222 | | - return; |
223 | | - } |
224 | | - |
225 | | - if ($this->whiteAfterLastChangedPart) { |
226 | | - $this->textNodes[$start]->whiteBefore = false; |
227 | | - } |
228 | | - |
229 | | - for ($i = $start; $i < $end; ++$i) { |
230 | | - $mod = new Modification(Modification::ADDED); |
231 | | - $mod->id = $this->newID; |
232 | | - $this->textNodes[$i]->modification = $mod; |
233 | | - } |
234 | | - if ($start < $end) { |
235 | | - $this->textNodes[$start]->modification->firstOfID = true; |
236 | | - } |
237 | | - ++$this->newID; |
238 | | - } |
239 | | - |
240 | | - public function handlePossibleChangedPart($leftstart, $leftend, $rightstart, $rightend) { |
241 | | - $i = $rightstart; |
242 | | - $j = $leftstart; |
243 | | - |
244 | | - if ($this->changedIDUsed) { |
245 | | - ++$this->changedID; |
246 | | - $this->changedIDUsed = false; |
247 | | - } |
248 | | - |
249 | | - $changes; |
250 | | - while ($i < $rightend) { |
251 | | - $acthis = new AncestorComparator($this->textNodes[$i]->getParentTree()); |
252 | | - $acother = new AncestorComparator($this->oldTextNodes[$j]->getParentTree()); |
253 | | - $result = $acthis->getResult($acother); |
254 | | - unset($acthis, $acother); |
255 | | - |
256 | | - if ( $result ) { |
257 | | - $mod = new Modification(Modification::CHANGED); |
258 | | - |
259 | | - if (!$this->changedIDUsed) { |
260 | | - $mod->firstOfID = true; |
261 | | - } else if (!is_null( $result ) && $result !== $this->changes) { |
262 | | - ++$this->changedID; |
263 | | - $mod->firstOfID = true; |
264 | | - } |
265 | | - |
266 | | - $mod->changes = $result; |
267 | | - $mod->id = $this->changedID; |
268 | | - |
269 | | - $this->textNodes[$i]->modification = $mod; |
270 | | - $this->changes = $result; |
271 | | - $this->changedIDUsed = true; |
272 | | - } else if ($this->changedIDUsed) { |
273 | | - ++$this->changedID; |
274 | | - $this->changedIDUsed = false; |
275 | | - } |
276 | | - ++$i; |
277 | | - ++$j; |
278 | | - } |
279 | | - } |
280 | | - |
281 | | - public function markAsDeleted($start, $end, $before) { |
282 | | - |
283 | | - if ($end <= $start) { |
284 | | - return; |
285 | | - } |
286 | | - |
287 | | - if ($before > 0 && $this->textNodes[$before - 1]->whiteAfter) { |
288 | | - $this->whiteAfterLastChangedPart = true; |
289 | | - } else { |
290 | | - $this->whiteAfterLastChangedPart = false; |
291 | | - } |
292 | | - |
293 | | - for ($i = $start; $i < $end; ++$i) { |
294 | | - $mod = new Modification(Modification::REMOVED); |
295 | | - $mod->id = $this->deletedID; |
296 | | - |
297 | | - // oldTextNodes is used here because we're going to move its deleted |
298 | | - // elements to this tree! |
299 | | - $this->oldTextNodes[$i]->modification = $mod; |
300 | | - } |
301 | | - $this->oldTextNodes[$start]->modification->firstOfID = true; |
302 | | - |
303 | | - $root = $this->oldTextNodes[$start]->getLastCommonParent($this->oldTextNodes[$end-1])->parent; |
304 | | - |
305 | | - $junk1 = $junk2 = null; |
306 | | - $deletedNodes = $root->getMinimalDeletedSet($this->deletedID, $junk1, $junk2); |
307 | | - |
308 | | - HTMLDiffer::diffDebug( "Minimal set of deleted nodes of size " . count($deletedNodes) . "\n" ); |
309 | | - |
310 | | - // Set prevLeaf to the leaf after which the old HTML needs to be |
311 | | - // inserted |
312 | | - if ($before > 0) { |
313 | | - $prevLeaf = $this->textNodes[$before - 1]; |
314 | | - } |
315 | | - // Set nextLeaf to the leaf before which the old HTML needs to be |
316 | | - // inserted |
317 | | - if ($before < count($this->textNodes)) { |
318 | | - $nextLeaf = $this->textNodes[$before]; |
319 | | - } |
320 | | - |
321 | | - while (count($deletedNodes) > 0) { |
322 | | - if (isset($prevLeaf)) { |
323 | | - $prevResult = $prevLeaf->getLastCommonParent($deletedNodes[0]); |
324 | | - } else { |
325 | | - $prevResult = new LastCommonParentResult(); |
326 | | - $prevResult->parent = $this->bodyNode; |
327 | | - $prevResult->indexInLastCommonParent = -1; |
328 | | - } |
329 | | - if (isset($nextleaf)) { |
330 | | - $nextResult = $nextLeaf->getLastCommonParent($deletedNodes[count($deletedNodes) - 1]); |
331 | | - } else { |
332 | | - $nextResult = new LastCommonParentResult(); |
333 | | - $nextResult->parent = $this->bodyNode; |
334 | | - $nextResult->indexInLastCommonParent = $this->bodyNode->getNbChildren(); |
335 | | - } |
336 | | - |
337 | | - if ($prevResult->lastCommonParentDepth == $nextResult->lastCommonParentDepth) { |
338 | | - // We need some metric to choose which way to add-... |
339 | | - if ($deletedNodes[0]->parent === $deletedNodes[count($deletedNodes) - 1]->parent |
340 | | - && $prevResult->parent === $nextResult->parent) { |
341 | | - // The difference is not in the parent |
342 | | - $prevResult->lastCommonParentDepth = $prevResult->lastCommonParentDepth + 1; |
343 | | - } else { |
344 | | - // The difference is in the parent, so compare them |
345 | | - // now THIS is tricky |
346 | | - $distancePrev = $deletedNodes[0]->parent->getMatchRatio($prevResult->parent); |
347 | | - $distanceNext = $deletedNodes[count($deletedNodes) - 1]->parent->getMatchRatio($nextResult->parent); |
348 | | - |
349 | | - if ($distancePrev <= $distanceNext) { |
350 | | - $prevResult->lastCommonParentDepth = $prevResult->lastCommonParentDepth + 1; |
351 | | - } else { |
352 | | - $nextResult->lastCommonParentDepth = $nextResult->lastCommonParentDepth + 1; |
353 | | - } |
354 | | - } |
355 | | - |
356 | | - } |
357 | | - |
358 | | - if ($prevResult->lastCommonParentDepth > $nextResult->lastCommonParentDepth) { |
359 | | - // Inserting at the front |
360 | | - if ($prevResult->splittingNeeded) { |
361 | | - $prevLeaf->parent->splitUntil($prevResult->parent, $prevLeaf, true); |
362 | | - } |
363 | | - $prevLeaf = $deletedNodes[0]->copyTree(); |
364 | | - unset($deletedNodes[0]); |
365 | | - $deletedNodes = array_values($deletedNodes); |
366 | | - $prevLeaf->setParent($prevResult->parent); |
367 | | - $prevResult->parent->addChildAbsolute($prevLeaf,$prevResult->indexInLastCommonParent + 1); |
368 | | - } else if ($prevResult->lastCommonParentDepth < $nextResult->lastCommonParentDepth) { |
369 | | - // Inserting at the back |
370 | | - if ($nextResult->splittingNeeded) { |
371 | | - $splitOccured = $nextLeaf->parent->splitUntil($nextResult->parent, $nextLeaf, false); |
372 | | - if ($splitOccured) { |
373 | | - // The place where to insert is shifted one place to the |
374 | | - // right |
375 | | - $nextResult->indexInLastCommonParent = $nextResult->indexInLastCommonParent + 1; |
376 | | - } |
377 | | - } |
378 | | - $nextLeaf = $deletedNodes[count(deletedNodes) - 1]->copyTree(); |
379 | | - unset($deletedNodes[count(deletedNodes) - 1]); |
380 | | - $deletedNodes = array_values($deletedNodes); |
381 | | - $nextLeaf->setParent($nextResult->parent); |
382 | | - $nextResult->parent->addChildAbsolute($nextLeaf,$nextResult->indexInLastCommonParent); |
383 | | - } |
384 | | - } |
385 | | - ++$this->deletedID; |
386 | | - } |
387 | | - |
388 | | - public function expandWhiteSpace() { |
389 | | - $this->bodyNode->expandWhiteSpace(); |
390 | | - } |
391 | | - |
392 | | - public function lengthNew(){ |
393 | | - return count($this->textNodes); |
394 | | - } |
395 | | - |
396 | | - public function lengthOld(){ |
397 | | - return count($this->oldTextNodes); |
398 | | - } |
399 | | -} |
400 | | - |
401 | | -class HTMLDiffer { |
402 | | - |
403 | | - private $output; |
404 | | - private static $debug = ''; |
405 | | - |
406 | | - function __construct($output) { |
407 | | - $this->output = $output; |
408 | | - } |
409 | | - |
410 | | - function htmlDiff($from, $to) { |
411 | | - wfProfileIn( __METHOD__ ); |
412 | | - // Create an XML parser |
413 | | - $xml_parser = xml_parser_create(''); |
414 | | - |
415 | | - $domfrom = new DomTreeBuilder(); |
416 | | - |
417 | | - // Set the functions to handle opening and closing tags |
418 | | - xml_set_element_handler($xml_parser, array($domfrom, "startElement"), array($domfrom, "endElement")); |
419 | | - |
420 | | - // Set the function to handle blocks of character data |
421 | | - xml_set_character_data_handler($xml_parser, array($domfrom, "characters")); |
422 | | - |
423 | | - HTMLDiffer::diffDebug( "Parsing " . strlen($from) . " characters worth of HTML\n" ); |
424 | | - if (!xml_parse($xml_parser, '<?xml version="1.0" encoding="UTF-8"?>'.Sanitizer::hackDocType().'<body>', false) |
425 | | - || !xml_parse($xml_parser, $from, false) |
426 | | - || !xml_parse($xml_parser, '</body>', true)){ |
427 | | - $error = xml_error_string(xml_get_error_code($xml_parser)); |
428 | | - $line = xml_get_current_line_number($xml_parser); |
429 | | - HTMLDiffer::diffDebug( "XML error: $error at line $line\n" ); |
430 | | - } |
431 | | - xml_parser_free($xml_parser); |
432 | | - unset($from); |
433 | | - |
434 | | - $xml_parser = xml_parser_create(''); |
435 | | - |
436 | | - $domto = new DomTreeBuilder(); |
437 | | - |
438 | | - // Set the functions to handle opening and closing tags |
439 | | - xml_set_element_handler($xml_parser, array($domto, "startElement"), array($domto, "endElement")); |
440 | | - |
441 | | - // Set the function to handle blocks of character data |
442 | | - xml_set_character_data_handler($xml_parser, array($domto, "characters")); |
443 | | - |
444 | | - HTMLDiffer::diffDebug( "Parsing " . strlen($to) . " characters worth of HTML\n" ); |
445 | | - if (!xml_parse($xml_parser, '<?xml version="1.0" encoding="UTF-8"?>'.Sanitizer::hackDocType().'<body>', false) |
446 | | - || !xml_parse($xml_parser, $to, false) |
447 | | - || !xml_parse($xml_parser, '</body>', true)){ |
448 | | - $error = xml_error_string(xml_get_error_code($xml_parser)); |
449 | | - $line = xml_get_current_line_number($xml_parser); |
450 | | - HTMLDiffer::diffDebug( "XML error: $error at line $line\n" ); |
451 | | - } |
452 | | - xml_parser_free($xml_parser); |
453 | | - unset($to); |
454 | | - |
455 | | - $diffengine = new WikiDiff3(); |
456 | | - $differences = $this->preProcess($diffengine->diff_range($domfrom->getDiffLines(), $domto->getDiffLines())); |
457 | | - unset($xml_parser, $diffengine); |
458 | | - |
459 | | - $domdiffer = new TextNodeDiffer($domto, $domfrom); |
460 | | - |
461 | | - $currentIndexLeft = 0; |
462 | | - $currentIndexRight = 0; |
463 | | - foreach ($differences as &$d) { |
464 | | - if ($d->leftstart > $currentIndexLeft) { |
465 | | - $domdiffer->handlePossibleChangedPart($currentIndexLeft, $d->leftstart, |
466 | | - $currentIndexRight, $d->rightstart); |
467 | | - } |
468 | | - if ($d->leftlength > 0) { |
469 | | - $domdiffer->markAsDeleted($d->leftstart, $d->leftend, $d->rightstart); |
470 | | - } |
471 | | - $domdiffer->markAsNew($d->rightstart, $d->rightend); |
472 | | - |
473 | | - $currentIndexLeft = $d->leftend; |
474 | | - $currentIndexRight = $d->rightend; |
475 | | - } |
476 | | - $oldLength = $domdiffer->lengthOld(); |
477 | | - if ($currentIndexLeft < $oldLength) { |
478 | | - $domdiffer->handlePossibleChangedPart($currentIndexLeft, $oldLength, $currentIndexRight, $domdiffer->lengthNew()); |
479 | | - } |
480 | | - $domdiffer->expandWhiteSpace(); |
481 | | - $output = new HTMLOutput('htmldiff', $this->output); |
482 | | - $output->parse($domdiffer->bodyNode); |
483 | | - wfProfileOut( __METHOD__ ); |
484 | | - } |
485 | | - |
486 | | - private function preProcess(/*array*/ $differences) { |
487 | | - $newRanges = array(); |
488 | | - |
489 | | - $nbDifferences = count($differences); |
490 | | - for ($i = 0; $i < $nbDifferences; ++$i) { |
491 | | - $leftStart = $differences[$i]->leftstart; |
492 | | - $leftEnd = $differences[$i]->leftend; |
493 | | - $rightStart = $differences[$i]->rightstart; |
494 | | - $rightEnd = $differences[$i]->rightend; |
495 | | - |
496 | | - $leftLength = $leftEnd - $leftStart; |
497 | | - $rightLength = $rightEnd - $rightStart; |
498 | | - |
499 | | - while ($i + 1 < $nbDifferences && self::score($leftLength, |
500 | | - $differences[$i + 1]->leftlength, |
501 | | - $rightLength, |
502 | | - $differences[$i + 1]->rightlength) |
503 | | - > ($differences[$i + 1]->leftstart - $leftEnd)) { |
504 | | - $leftEnd = $differences[$i + 1]->leftend; |
505 | | - $rightEnd = $differences[$i + 1]->rightend; |
506 | | - $leftLength = $leftEnd - $leftStart; |
507 | | - $rightLength = $rightEnd - $rightStart; |
508 | | - ++$i; |
509 | | - } |
510 | | - $newRanges[] = new RangeDifference($leftStart, $leftEnd, $rightStart, $rightEnd); |
511 | | - } |
512 | | - return $newRanges; |
513 | | - } |
514 | | - |
515 | | - /** |
516 | | - * Heuristic to merge differences for readability. |
517 | | - */ |
518 | | - public static function score($ll, $nll, $rl, $nrl) { |
519 | | - if (($ll == 0 && $nll == 0) |
520 | | - || ($rl == 0 && $nrl == 0)) { |
521 | | - return 0; |
522 | | - } |
523 | | - $numbers = array($ll, $nll, $rl, $nrl); |
524 | | - $d = 0; |
525 | | - foreach ($numbers as &$number) { |
526 | | - while ($number > 3) { |
527 | | - $d += 3; |
528 | | - $number -= 3; |
529 | | - $number *= 0.5; |
530 | | - } |
531 | | - $d += $number; |
532 | | - |
533 | | - } |
534 | | - return $d / (1.5 * count($numbers)); |
535 | | - } |
536 | | - |
537 | | - /** |
538 | | - * Add to debug output |
539 | | - * @param string $str Debug output |
540 | | - */ |
541 | | - public static function diffDebug( $str ) { |
542 | | - self :: $debug .= $str; |
543 | | - } |
544 | | - |
545 | | - /** |
546 | | - * Get debug output |
547 | | - * @return string |
548 | | - */ |
549 | | - public static function getDebugOutput() { |
550 | | - return self :: $debug; |
551 | | - } |
552 | | - |
553 | | -} |
554 | | - |
555 | | -class TextOnlyComparator { |
556 | | - |
557 | | - public $leafs = array(); |
558 | | - |
559 | | - function _construct(TagNode $tree) { |
560 | | - $this->addRecursive($tree); |
561 | | - $this->leafs = array_map(array('TextNode','toDiffLine'), $this->leafs); |
562 | | - } |
563 | | - |
564 | | - private function addRecursive(TagNode $tree) { |
565 | | - foreach ($tree->children as &$child) { |
566 | | - if ($child instanceof TagNode) { |
567 | | - $this->addRecursive($child); |
568 | | - } else if ($child instanceof TextNode) { |
569 | | - $this->leafs[] = $node; |
570 | | - } |
571 | | - } |
572 | | - } |
573 | | - |
574 | | - public function getMatchRatio(TextOnlyComparator $other) { |
575 | | - $nbOthers = count($other->leafs); |
576 | | - $nbThis = count($this->leafs); |
577 | | - if($nbOthers == 0 || $nbThis == 0){ |
578 | | - return -log(0); |
579 | | - } |
580 | | - |
581 | | - $diffengine = new WikiDiff3(25000, 1.35); |
582 | | - $diffengine->diff($this->leafs, $other->leafs); |
583 | | - |
584 | | - $lcsLength = $diffengine->getLcsLength(); |
585 | | - |
586 | | - $distanceThis = $nbThis-$lcsLength; |
587 | | - |
588 | | - return (2.0 - $lcsLength/$nbOthers - $lcsLength/$nbThis) / 2.0; |
589 | | - } |
590 | | -} |
591 | | - |
592 | | -/** |
593 | | - * A comparator used when calculating the difference in ancestry of two Nodes. |
594 | | - */ |
595 | | -class AncestorComparator { |
596 | | - |
597 | | - public $ancestors; |
598 | | - public $ancestorsText; |
599 | | - |
600 | | - function __construct(/*array*/ $ancestors) { |
601 | | - $this->ancestors = $ancestors; |
602 | | - $this->ancestorsText = array_map(array('TagNode','toDiffLine'), $ancestors); |
603 | | - } |
604 | | - |
605 | | - public $compareTxt = ""; |
606 | | - |
607 | | - public function getResult(AncestorComparator $other) { |
608 | | - |
609 | | - $diffengine = new WikiDiff3(10000, 1.35); |
610 | | - $differences = $diffengine->diff_range($other->ancestorsText,$this->ancestorsText); |
611 | | - |
612 | | - if (count($differences) == 0){ |
613 | | - return null; |
614 | | - } |
615 | | - $changeTxt = new ChangeTextGenerator($this, $other); |
616 | | - |
617 | | - return $changeTxt->getChanged($differences)->toString();; |
618 | | - } |
619 | | -} |
620 | | - |
621 | | -class ChangeTextGenerator { |
622 | | - |
623 | | - private $ancestorComparator; |
624 | | - private $other; |
625 | | - |
626 | | - private $factory; |
627 | | - |
628 | | - function __construct(AncestorComparator $ancestorComparator, AncestorComparator $other) { |
629 | | - $this->ancestorComparator = $ancestorComparator; |
630 | | - $this->other = $other; |
631 | | - $this->factory = new TagToStringFactory(); |
632 | | - } |
633 | | - |
634 | | - public function getChanged(/*array*/ $differences) { |
635 | | - $txt = new ChangeText; |
636 | | - $rootlistopened = false; |
637 | | - if (count($differences) > 1) { |
638 | | - $txt->addHtml('<ul class="changelist">'); |
639 | | - $rootlistopened = true; |
640 | | - } |
641 | | - $nbDifferences = count($differences); |
642 | | - for ($j = 0; $j < $nbDifferences; ++$j) { |
643 | | - $d = $differences[$j]; |
644 | | - $lvl1listopened = false; |
645 | | - if ($rootlistopened) { |
646 | | - $txt->addHtml('<li>'); |
647 | | - } |
648 | | - if ($d->leftlength + $d->rightlength > 1) { |
649 | | - $txt->addHtml('<ul class="changelist">'); |
650 | | - $lvl1listopened = true; |
651 | | - } |
652 | | - // left are the old ones |
653 | | - for ($i = $d->leftstart; $i < $d->leftend; ++$i) { |
654 | | - if ($lvl1listopened){ |
655 | | - $txt->addHtml('<li>'); |
656 | | - } |
657 | | - // add a bullet for a old tag |
658 | | - $this->addTagOld($txt, $this->other->ancestors[$i]); |
659 | | - if ($lvl1listopened){ |
660 | | - $txt->addHtml('</li>'); |
661 | | - } |
662 | | - } |
663 | | - // right are the new ones |
664 | | - for ($i = $d->rightstart; $i < $d->rightend; ++$i) { |
665 | | - if ($lvl1listopened){ |
666 | | - $txt->addHtml('<li>'); |
667 | | - } |
668 | | - // add a bullet for a new tag |
669 | | - $this->addTagNew($txt, $this->ancestorComparator->ancestors[$i]); |
670 | | - |
671 | | - if ($lvl1listopened){ |
672 | | - $txt->addHtml('</li>'); |
673 | | - } |
674 | | - } |
675 | | - if ($lvl1listopened) { |
676 | | - $txt->addHtml('</ul>'); |
677 | | - } |
678 | | - if ($rootlistopened) { |
679 | | - $txt->addHtml('</li>'); |
680 | | - } |
681 | | - } |
682 | | - if ($rootlistopened) { |
683 | | - $txt->addHtml('</ul>'); |
684 | | - } |
685 | | - return $txt; |
686 | | - } |
687 | | - |
688 | | - private function addTagOld(ChangeText $txt, TagNode $ancestor) { |
689 | | - $this->factory->create($ancestor)->getRemovedDescription($txt); |
690 | | - } |
691 | | - |
692 | | - private function addTagNew(ChangeText $txt, TagNode $ancestor) { |
693 | | - $this->factory->create($ancestor)->getAddedDescription($txt); |
694 | | - } |
695 | | -} |
696 | | - |
697 | | -class ChangeText { |
698 | | - |
699 | | - private $txt = ""; |
700 | | - |
701 | | - public function addHtml($s) { |
702 | | - $this->txt .= $s; |
703 | | - } |
704 | | - |
705 | | - public function toString() { |
706 | | - return $this->txt; |
707 | | - } |
708 | | -} |
709 | | - |
710 | | -class TagToStringFactory { |
711 | | - |
712 | | - private static $containerTags = array('html', 'body', 'p', 'blockquote', |
713 | | - 'h1', 'h2', 'h3', 'h4', 'h5', 'pre', 'div', 'ul', 'ol', 'li', |
714 | | - 'table', 'tbody', 'tr', 'td', 'th', 'br', 'hr', 'code', 'dl', |
715 | | - 'dt', 'dd', 'input', 'form', 'img', 'span', 'a'); |
716 | | - |
717 | | - private static $styleTags = array('i', 'b', 'strong', 'em', 'font', |
718 | | - 'big', 'del', 'tt', 'sub', 'sup', 'strike'); |
719 | | - |
720 | | - const MOVED = 1; |
721 | | - const STYLE = 2; |
722 | | - const UNKNOWN = 4; |
723 | | - |
724 | | - public function create(TagNode $node) { |
725 | | - $sem = $this->getChangeSemantic($node->qName); |
726 | | - if (strcasecmp($node->qName,'a') == 0) { |
727 | | - return new AnchorToString($node, $sem); |
728 | | - } |
729 | | - if (strcasecmp($node->qName,'img') == 0) { |
730 | | - return new NoContentTagToString($node, $sem); |
731 | | - } |
732 | | - return new TagToString($node, $sem); |
733 | | - } |
734 | | - |
735 | | - protected function getChangeSemantic($qname) { |
736 | | - if (in_array(strtolower($qname),self::$containerTags)) { |
737 | | - return self::MOVED; |
738 | | - } |
739 | | - if (in_array(strtolower($qname),self::$styleTags)) { |
740 | | - return self::STYLE; |
741 | | - } |
742 | | - return self::UNKNOWN; |
743 | | - } |
744 | | -} |
745 | | - |
746 | | -class TagToString { |
747 | | - |
748 | | - protected $node; |
749 | | - |
750 | | - protected $sem; |
751 | | - |
752 | | - function __construct(TagNode $node, $sem) { |
753 | | - $this->node = $node; |
754 | | - $this->sem = $sem; |
755 | | - } |
756 | | - |
757 | | - public function getRemovedDescription(ChangeText $txt) { |
758 | | - $tagDescription = wfMsgExt('diff-' . $this->node->qName, 'parseinline' ); |
759 | | - if( wfEmptyMsg( 'diff-' . $this->node->qName, $tagDescription ) ){ |
760 | | - $tagDescription = "<" . $this->node->qName . ">"; |
761 | | - } |
762 | | - if ($this->sem == TagToStringFactory::MOVED) { |
763 | | - $txt->addHtml( wfMsgExt( 'diff-movedoutof', 'parseinline', $tagDescription ) ); |
764 | | - } else if ($this->sem == TagToStringFactory::STYLE) { |
765 | | - $txt->addHtml( wfMsgExt( 'diff-styleremoved' , 'parseinline', $tagDescription ) ); |
766 | | - } else { |
767 | | - $txt->addHtml( wfMsgExt( 'diff-removed' , 'parseinline', $tagDescription ) ); |
768 | | - } |
769 | | - $this->addAttributes($txt, $this->node->attributes); |
770 | | - $txt->addHtml('.'); |
771 | | - } |
772 | | - |
773 | | - public function getAddedDescription(ChangeText $txt) { |
774 | | - $tagDescription = wfMsgExt('diff-' . $this->node->qName, 'parseinline' ); |
775 | | - if( wfEmptyMsg( 'diff-' . $this->node->qName, $tagDescription ) ){ |
776 | | - $tagDescription = "<" . $this->node->qName . ">"; |
777 | | - } |
778 | | - if ($this->sem == TagToStringFactory::MOVED) { |
779 | | - $txt->addHtml( wfMsgExt( 'diff-movedto' , 'parseinline', $tagDescription) ); |
780 | | - } else if ($this->sem == TagToStringFactory::STYLE) { |
781 | | - $txt->addHtml( wfMsgExt( 'diff-styleadded', 'parseinline', $tagDescription ) ); |
782 | | - } else { |
783 | | - $txt->addHtml( wfMsgExt( 'diff-added', 'parseinline', $tagDescription ) ); |
784 | | - } |
785 | | - $this->addAttributes($txt, $this->node->attributes); |
786 | | - $txt->addHtml('.'); |
787 | | - } |
788 | | - |
789 | | - protected function addAttributes(ChangeText $txt, array $attributes) { |
790 | | - if (count($attributes) < 1) { |
791 | | - return; |
792 | | - } |
793 | | - $firstOne = true; |
794 | | - $nbAttributes_min_1 = count($attributes)-1; |
795 | | - $keys = array_keys($attributes); |
796 | | - for ($i=0;$i<$nbAttributes_min_1;$i++) { |
797 | | - $key = $keys[$i]; |
798 | | - $attr = $attributes[$key]; |
799 | | - if($firstOne) { |
800 | | - $firstOne = false; |
801 | | - $txt->addHtml( wfMsgExt('diff-with', 'escapenoentities', $this->translateArgument($key), htmlspecialchars($attr) ) ); |
802 | | - continue; |
803 | | - } |
804 | | - $txt->addHtml( wfMsgExt( 'comma-separator', 'escapenoentities' ) . |
805 | | - wfMsgExt( 'diff-with-additional', 'escapenoentities', |
806 | | - $this->translateArgument( $key ), htmlspecialchars( $attr ) ) |
807 | | - ); |
808 | | - } |
809 | | - |
810 | | - if ($nbAttributes_min_1 > 0) { |
811 | | - $txt->addHtml( wfMsgExt( 'diff-with-final', 'escapenoentities', |
812 | | - $this->translateArgument($keys[$nbAttributes_min_1]), |
813 | | - htmlspecialchars($attributes[$keys[$nbAttributes_min_1]]) ) ); |
814 | | - } |
815 | | - } |
816 | | - |
817 | | - protected function translateArgument($name) { |
818 | | - $translation = wfMsgExt('diff-' . $name, 'parseinline' ); |
819 | | - if ( wfEmptyMsg( 'diff-' . $name, $translation ) ) { |
820 | | - $translation = "<" . $name . ">";; |
821 | | - } |
822 | | - return htmlspecialchars( $translation ); |
823 | | - } |
824 | | -} |
825 | | - |
826 | | -class NoContentTagToString extends TagToString { |
827 | | - |
828 | | - function __construct(TagNode $node, $sem) { |
829 | | - parent::__construct($node, $sem); |
830 | | - } |
831 | | - |
832 | | - public function getAddedDescription(ChangeText $txt) { |
833 | | - $tagDescription = wfMsgExt('diff-' . $this->node->qName, 'parseinline' ); |
834 | | - if( wfEmptyMsg( 'diff-' . $this->node->qName, $tagDescription ) ){ |
835 | | - $tagDescription = "<" . $this->node->qName . ">"; |
836 | | - } |
837 | | - $txt->addHtml( wfMsgExt('diff-changedto', 'parseinline', $tagDescription ) ); |
838 | | - $this->addAttributes($txt, $this->node->attributes); |
839 | | - $txt->addHtml('.'); |
840 | | - } |
841 | | - |
842 | | - public function getRemovedDescription(ChangeText $txt) { |
843 | | - $tagDescription = wfMsgExt('diff-' . $this->node->qName, 'parseinline' ); |
844 | | - if( wfEmptyMsg( 'diff-' . $this->node->qName, $tagDescription ) ){ |
845 | | - $tagDescription = "<" . $this->node->qName . ">"; |
846 | | - } |
847 | | - $txt->addHtml( wfMsgExt('diff-changedfrom', 'parseinline', $tagDescription ) ); |
848 | | - $this->addAttributes($txt, $this->node->attributes); |
849 | | - $txt->addHtml('.'); |
850 | | - } |
851 | | -} |
852 | | - |
853 | | -class AnchorToString extends TagToString { |
854 | | - |
855 | | - function __construct(TagNode $node, $sem) { |
856 | | - parent::__construct($node, $sem); |
857 | | - } |
858 | | - |
859 | | - protected function addAttributes(ChangeText $txt, array $attributes) { |
860 | | - if (array_key_exists('href', $attributes)) { |
861 | | - $txt->addHtml(' ' . wfMsgExt( 'diff-withdestination', 'parseinline', htmlspecialchars($attributes['href']) ) ); |
862 | | - unset($attributes['href']); |
863 | | - } |
864 | | - parent::addAttributes($txt, $attributes); |
865 | | - } |
866 | | -} |
867 | | - |
868 | | -/** |
869 | | - * Takes a branch root and creates an HTML file for it. |
870 | | - */ |
871 | | -class HTMLOutput{ |
872 | | - |
873 | | - private $prefix; |
874 | | - private $handler; |
875 | | - |
876 | | - function __construct($prefix, $handler) { |
877 | | - $this->prefix = $prefix; |
878 | | - $this->handler = $handler; |
879 | | - } |
880 | | - |
881 | | - public function parse(TagNode $node) { |
882 | | - $handler = &$this->handler; |
883 | | - |
884 | | - if (strcasecmp($node->qName, 'img') != 0 && strcasecmp($node->qName, 'body') != 0) { |
885 | | - $handler->startElement($node->qName, $node->attributes); |
886 | | - } |
887 | | - |
888 | | - $newStarted = false; |
889 | | - $remStarted = false; |
890 | | - $changeStarted = false; |
891 | | - $changeTXT = ''; |
892 | | - |
893 | | - foreach ($node->children as &$child) { |
894 | | - if ($child instanceof TagNode) { |
895 | | - if ($newStarted) { |
896 | | - $handler->endElement('span'); |
897 | | - $newStarted = false; |
898 | | - } else if ($changeStarted) { |
899 | | - $handler->endElement('span'); |
900 | | - $changeStarted = false; |
901 | | - } else if ($remStarted) { |
902 | | - $handler->endElement('span'); |
903 | | - $remStarted = false; |
904 | | - } |
905 | | - $this->parse($child); |
906 | | - } else if ($child instanceof TextNode) { |
907 | | - $mod = $child->modification; |
908 | | - |
909 | | - if ($newStarted && ($mod->type != Modification::ADDED || $mod->firstOfID)) { |
910 | | - $handler->endElement('span'); |
911 | | - $newStarted = false; |
912 | | - } else if ($changeStarted && ($mod->type != Modification::CHANGED |
913 | | - || $mod->changes != $changeTXT || $mod->firstOfID)) { |
914 | | - $handler->endElement('span'); |
915 | | - $changeStarted = false; |
916 | | - } else if ($remStarted && ($mod->type != Modification::REMOVED || $mod ->firstOfID)) { |
917 | | - $handler->endElement('span'); |
918 | | - $remStarted = false; |
919 | | - } |
920 | | - |
921 | | - // no else because a removed part can just be closed and a new |
922 | | - // part can start |
923 | | - if (!$newStarted && $mod->type == Modification::ADDED) { |
924 | | - $attrs = array('class' => 'diff-html-added'); |
925 | | - if ($mod->firstOfID) { |
926 | | - $attrs['id'] = "added-{$this->prefix}-{$mod->id}"; |
927 | | - } |
928 | | - $handler->startElement('span', $attrs); |
929 | | - $newStarted = true; |
930 | | - } else if (!$changeStarted && $mod->type == Modification::CHANGED) { |
931 | | - $attrs = array('class' => 'diff-html-changed'); |
932 | | - if ($mod->firstOfID) { |
933 | | - $attrs['id'] = "changed-{$this->prefix}-{$mod->id}"; |
934 | | - } |
935 | | - $handler->startElement('span', $attrs); |
936 | | - |
937 | | - //tooltip |
938 | | - $handler->startElement('span', array('class' => 'tip')); |
939 | | - $handler->html($mod->changes); |
940 | | - $handler->endElement('span'); |
941 | | - |
942 | | - $changeStarted = true; |
943 | | - $changeTXT = $mod->changes; |
944 | | - } else if (!$remStarted && $mod->type == Modification::REMOVED) { |
945 | | - $attrs = array('class'=>'diff-html-removed'); |
946 | | - if ($mod->firstOfID) { |
947 | | - $attrs['id'] = "removed-{$this->prefix}-{$mod->id}"; |
948 | | - } |
949 | | - $handler->startElement('span', $attrs); |
950 | | - $remStarted = true; |
951 | | - } |
952 | | - |
953 | | - $chars = $child->text; |
954 | | - |
955 | | - if ($child instanceof ImageNode) { |
956 | | - $this->writeImage($child); |
957 | | - } else { |
958 | | - $handler->characters($chars); |
959 | | - } |
960 | | - } |
961 | | - } |
962 | | - |
963 | | - if ($newStarted) { |
964 | | - $handler->endElement('span'); |
965 | | - $newStarted = false; |
966 | | - } else if ($changeStarted) { |
967 | | - $handler->endElement('span'); |
968 | | - $changeStarted = false; |
969 | | - } else if ($remStarted) { |
970 | | - $handler->endElement('span'); |
971 | | - $remStarted = false; |
972 | | - } |
973 | | - |
974 | | - if (strcasecmp($node->qName, 'img') != 0 |
975 | | - && strcasecmp($node->qName, 'body') != 0) { |
976 | | - $handler->endElement($node->qName); |
977 | | - } |
978 | | - } |
979 | | - |
980 | | - private function writeImage(ImageNode $imgNode) { |
981 | | - $attrs = $imgNode->attributes; |
982 | | - $this->handler->startElement('img', $attrs); |
983 | | - $this->handler->endElement('img'); |
984 | | - } |
985 | | -} |
986 | | - |
987 | | -class DelegatingContentHandler { |
988 | | - |
989 | | - private $delegate; |
990 | | - |
991 | | - function __construct($delegate) { |
992 | | - $this->delegate = $delegate; |
993 | | - } |
994 | | - |
995 | | - function startElement($qname, /*array*/ $arguments) { |
996 | | - $this->delegate->addHtml(Xml::openElement($qname, $arguments)); |
997 | | - } |
998 | | - |
999 | | - function endElement($qname){ |
1000 | | - $this->delegate->addHtml(Xml::closeElement($qname)); |
1001 | | - } |
1002 | | - |
1003 | | - function characters($chars){ |
1004 | | - $this->delegate->addHtml(htmlspecialchars($chars)); |
1005 | | - } |
1006 | | - |
1007 | | - function html($html){ |
1008 | | - $this->delegate->addHtml($html); |
1009 | | - } |
1010 | | -} |
Index: trunk/phase3/includes/diff/DifferenceInterface.php |
— | — | @@ -28,7 +28,6 @@ |
29 | 29 | var $mRevisionsLoaded = false; // Have the revisions been loaded |
30 | 30 | var $mTextLoaded = 0; // How many text blobs have been loaded, 0, 1 or 2? |
31 | 31 | var $mCacheHit = false; // Was the diff fetched from cache? |
32 | | - var $htmldiff; |
33 | 32 | |
34 | 33 | /** |
35 | 34 | * Set this to true to add debug info to the HTML output. |
— | — | @@ -51,11 +50,10 @@ |
52 | 51 | * @param $new String: either 'prev' or 'next'. |
53 | 52 | * @param $rcid Integer: ??? FIXME (default 0) |
54 | 53 | * @param $refreshCache boolean If set, refreshes the diff cache |
55 | | - * @param $htmldiff boolean If set, output using HTMLDiff instead of raw wikicode diff |
56 | 54 | * @param $unhide boolean If set, allow viewing deleted revs |
57 | 55 | */ |
58 | 56 | function __construct( $titleObj = null, $old = 0, $new = 0, $rcid = 0, |
59 | | - $refreshCache = false, $htmldiff = false, $unhide = false ) |
| 57 | + $refreshCache = false, $unhide = false ) |
60 | 58 | { |
61 | 59 | if ( $titleObj ) { |
62 | 60 | $this->mTitle = $titleObj; |
— | — | @@ -87,7 +85,6 @@ |
88 | 86 | } |
89 | 87 | $this->mRcidMarkPatrolled = intval($rcid); # force it to be an integer |
90 | 88 | $this->mRefreshCache = $refreshCache; |
91 | | - $this->htmldiff = $htmldiff; |
92 | 89 | $this->unhide = $unhide; |
93 | 90 | } |
94 | 91 | |
— | — | @@ -112,7 +109,7 @@ |
113 | 110 | } |
114 | 111 | |
115 | 112 | function showDiffPage( $diffOnly = false ) { |
116 | | - global $wgUser, $wgOut, $wgUseExternalEditor, $wgUseRCPatrol, $wgEnableHtmlDiff; |
| 113 | + global $wgUser, $wgOut, $wgUseExternalEditor, $wgUseRCPatrol; |
117 | 114 | wfProfileIn( __METHOD__ ); |
118 | 115 | |
119 | 116 | |
— | — | @@ -264,12 +261,6 @@ |
265 | 262 | $query['diffonly'] = $diffOnly; |
266 | 263 | } |
267 | 264 | |
268 | | - $htmldiffarg = $this->htmlDiffArgument(); |
269 | | - |
270 | | - if( $htmldiffarg ) { |
271 | | - $query['htmldiff'] = $htmldiffarg['htmldiff']; |
272 | | - } |
273 | | - |
274 | 265 | # Make "previous revision link" |
275 | 266 | $query['diff'] = 'prev'; |
276 | 267 | $query['oldid'] = $this->mOldid; |
— | — | @@ -401,47 +392,8 @@ |
402 | 393 | $msg = $suppressed ? 'rev-suppressed-unhide-diff' : 'rev-deleted-unhide-diff'; |
403 | 394 | $wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1</div>\n", array( $msg, $link ) ); |
404 | 395 | } |
405 | | - # Otherwise, output the HTML diff if requested... |
406 | | - } else if( $wgEnableHtmlDiff && $this->htmldiff ) { |
407 | | - $multi = $this->getMultiNotice(); |
408 | | - $wgOut->addHTML( '<div class="diff-switchtype">' . $sk->link( |
409 | | - $this->mTitle, |
410 | | - wfMsgHtml( 'wikicodecomparison' ), |
411 | | - array( |
412 | | - 'id' => 'differences-switchtype' |
413 | | - ), |
414 | | - array( |
415 | | - 'diff' => $this->mNewid, |
416 | | - 'oldid' => $this->mOldid, |
417 | | - 'htmldiff' => 0 |
418 | | - ), |
419 | | - array( |
420 | | - 'known', |
421 | | - 'noclasses' |
422 | | - ) |
423 | | - ) . '</div>'); |
424 | | - $wgOut->addHTML( $this->addHeader( '', $oldHeader, $newHeader, $multi ) ); |
425 | | - # Add deletion notice if the user is viewing deleted content |
426 | | - if( $deleted ) { |
427 | | - $msg = $suppressed ? 'rev-suppressed-diff-view' : 'rev-deleted-diff-view'; |
428 | | - $wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1</div>\n", $msg ); |
429 | | - } |
430 | | - $this->renderHtmlDiff(); |
431 | 396 | # Otherwise, output a regular diff... |
432 | 397 | } else { |
433 | | - if( $wgEnableHtmlDiff ) { |
434 | | - $wgOut->addHTML( '<div class="diff-switchtype">' . $sk->link( |
435 | | - $this->mTitle, |
436 | | - wfMsgHtml( 'visualcomparison' ), |
437 | | - array( 'id' => 'differences-switchtype' ), |
438 | | - array( |
439 | | - 'diff' => $this->mNewid, |
440 | | - 'oldid' => $this->mOldid, |
441 | | - 'htmldiff' => 1 |
442 | | - ), |
443 | | - array( 'known', 'noclasses' ) |
444 | | - ) . '</div>'); |
445 | | - } |
446 | 398 | # Add deletion notice if the user is viewing deleted content |
447 | 399 | $notice = ''; |
448 | 400 | if( $deleted ) { |
— | — | @@ -517,70 +469,6 @@ |
518 | 470 | wfProfileOut( __METHOD__ ); |
519 | 471 | } |
520 | 472 | |
521 | | - |
522 | | - function renderHtmlDiff() { |
523 | | - global $wgOut, $wgParser, $wgDebugComments; |
524 | | - wfProfileIn( __METHOD__ ); |
525 | | - |
526 | | - $this->showDiffStyle(); |
527 | | - |
528 | | - $wgOut->addHTML( '<h2>'.wfMsgHtml( 'visual-comparison' )."</h2>\n" ); |
529 | | - #add deleted rev tag if needed |
530 | | - if( !$this->mNewRev->userCan(Revision::DELETED_TEXT) ) { |
531 | | - $wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1</div>\n", 'rev-deleted-text-permission' ); |
532 | | - } else if( $this->mNewRev->isDeleted(Revision::DELETED_TEXT) ) { |
533 | | - $wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1</div>\n", 'rev-deleted-text-view' ); |
534 | | - } |
535 | | - |
536 | | - if( !$this->mNewRev->isCurrent() ) { |
537 | | - $oldEditSectionSetting = $wgOut->parserOptions()->setEditSection( false ); |
538 | | - } |
539 | | - |
540 | | - $this->loadText(); |
541 | | - |
542 | | - // Old revision |
543 | | - if( is_object( $this->mOldRev ) ) { |
544 | | - $wgOut->setRevisionId( $this->mOldRev->getId() ); |
545 | | - } |
546 | | - |
547 | | - $popts = $wgOut->parserOptions(); |
548 | | - $oldTidy = $popts->setTidy( true ); |
549 | | - $popts->setEditSection( false ); |
550 | | - |
551 | | - $parserOutput = $wgParser->parse( $this->mOldtext, $this->getTitle(), $popts, true, true, $wgOut->getRevisionId() ); |
552 | | - $popts->setTidy( $oldTidy ); |
553 | | - |
554 | | - //only for new? |
555 | | - //$wgOut->addParserOutputNoText( $parserOutput ); |
556 | | - $oldHtml = $parserOutput->getText(); |
557 | | - wfRunHooks( 'OutputPageBeforeHTML', array( &$wgOut, &$oldHtml ) ); |
558 | | - |
559 | | - // New revision |
560 | | - if( is_object( $this->mNewRev ) ) { |
561 | | - $wgOut->setRevisionId( $this->mNewRev->getId() ); |
562 | | - } |
563 | | - |
564 | | - $popts = $wgOut->parserOptions(); |
565 | | - $oldTidy = $popts->setTidy( true ); |
566 | | - |
567 | | - $parserOutput = $wgParser->parse( $this->mNewtext, $this->getTitle(), $popts, true, true, $wgOut->getRevisionId() ); |
568 | | - $popts->setTidy( $oldTidy ); |
569 | | - |
570 | | - $wgOut->addParserOutputNoText( $parserOutput ); |
571 | | - $newHtml = $parserOutput->getText(); |
572 | | - wfRunHooks( 'OutputPageBeforeHTML', array( &$wgOut, &$newHtml ) ); |
573 | | - |
574 | | - unset($parserOutput, $popts); |
575 | | - |
576 | | - $differ = new HTMLDiffer(new DelegatingContentHandler($wgOut)); |
577 | | - $differ->htmlDiff($oldHtml, $newHtml); |
578 | | - if ( $wgDebugComments ) { |
579 | | - $wgOut->addHTML( "\n<!-- HtmlDiff Debug Output:\n" . HTMLDiffer::getDebugOutput() . " End Debug -->" ); |
580 | | - } |
581 | | - |
582 | | - wfProfileOut( __METHOD__ ); |
583 | | - } |
584 | | - |
585 | 473 | /** |
586 | 474 | * Show the first revision of an article. Uses normal diff headers in |
587 | 475 | * contrast to normal "old revision" display style. |
— | — | @@ -629,7 +517,6 @@ |
630 | 518 | array( |
631 | 519 | 'diff' => 'next', |
632 | 520 | 'oldid' => $this->mNewid, |
633 | | - $this->htmlDiffArgument() |
634 | 521 | ), |
635 | 522 | array( |
636 | 523 | 'known', |
— | — | @@ -648,19 +535,6 @@ |
649 | 536 | wfProfileOut( __METHOD__ ); |
650 | 537 | } |
651 | 538 | |
652 | | - function htmlDiffArgument(){ |
653 | | - global $wgEnableHtmlDiff; |
654 | | - if($wgEnableHtmlDiff){ |
655 | | - if($this->htmldiff){ |
656 | | - return array( 'htmldiff' => 1 ); |
657 | | - }else{ |
658 | | - return array( 'htmldiff' => 0 ); |
659 | | - } |
660 | | - }else{ |
661 | | - return array(); |
662 | | - } |
663 | | - } |
664 | | - |
665 | 539 | /** |
666 | 540 | * Get the diff text, send it to $wgOut |
667 | 541 | * Returns false if the diff could not be generated, otherwise returns true |
Index: trunk/phase3/includes/HistoryPage.php |
— | — | @@ -359,7 +359,7 @@ |
360 | 360 | * @return string HTML output |
361 | 361 | */ |
362 | 362 | function getStartBody() { |
363 | | - global $wgScript, $wgEnableHtmlDiff, $wgUser, $wgOut, $wgContLang; |
| 363 | + global $wgScript, $wgUser, $wgOut, $wgContLang; |
364 | 364 | $this->lastRow = false; |
365 | 365 | $this->counter = 1; |
366 | 366 | $this->oldIdChecked = 0; |
— | — | @@ -385,34 +385,13 @@ |
386 | 386 | wfMsg( 'showhideselectedversions' ) |
387 | 387 | ) . "\n"; |
388 | 388 | } |
389 | | - if( $wgEnableHtmlDiff ) { |
390 | | - $this->buttons .= Xml::element( 'button', |
391 | | - array( |
392 | | - 'type' => 'submit', |
393 | | - 'name' => 'htmldiff', |
394 | | - 'value' => '1', |
395 | | - 'class' => 'historysubmit', |
396 | | - 'accesskey' => wfMsg( 'accesskey-visualcomparison' ), |
397 | | - 'title' => wfMsg( 'tooltip-compareselectedversions' ), |
398 | | - ), |
399 | | - wfMsg( 'visualcomparison') |
400 | | - ) . "\n"; |
401 | | - $this->buttons .= $this->submitButton( wfMsg( 'wikicodecomparison'), |
402 | | - array( |
403 | | - 'class' => 'historysubmit', |
404 | | - 'accesskey' => wfMsg( 'accesskey-compareselectedversions' ), |
405 | | - 'title' => wfMsg( 'tooltip-compareselectedversions' ), |
406 | | - ) |
407 | | - ) . "\n"; |
408 | | - } else { |
409 | | - $this->buttons .= $this->submitButton( wfMsg( 'compareselectedversions'), |
410 | | - array( |
411 | | - 'class' => 'historysubmit', |
412 | | - 'accesskey' => wfMsg( 'accesskey-compareselectedversions' ), |
413 | | - 'title' => wfMsg( 'tooltip-compareselectedversions' ), |
414 | | - ) |
415 | | - ) . "\n"; |
416 | | - } |
| 389 | + $this->buttons .= $this->submitButton( wfMsg( 'compareselectedversions'), |
| 390 | + array( |
| 391 | + 'class' => 'historysubmit', |
| 392 | + 'accesskey' => wfMsg( 'accesskey-compareselectedversions' ), |
| 393 | + 'title' => wfMsg( 'tooltip-compareselectedversions' ), |
| 394 | + ) |
| 395 | + ) . "\n"; |
417 | 396 | $this->buttons .= '</div>'; |
418 | 397 | $s .= $this->buttons . '<ul id="pagehistory">' . "\n"; |
419 | 398 | return $s; |
Index: trunk/phase3/includes/Article.php |
— | — | @@ -943,11 +943,10 @@ |
944 | 944 | $rcid = $wgRequest->getVal( 'rcid' ); |
945 | 945 | $diffOnly = $wgRequest->getBool( 'diffonly', $wgUser->getOption( 'diffonly' ) ); |
946 | 946 | $purge = $wgRequest->getVal( 'action' ) == 'purge'; |
947 | | - $htmldiff = $wgRequest->getBool( 'htmldiff' ); |
948 | 947 | $unhide = $wgRequest->getInt('unhide') == 1; |
949 | 948 | $oldid = $this->getOldID(); |
950 | 949 | |
951 | | - $de = new DifferenceEngine( $this->mTitle, $oldid, $diff, $rcid, $purge, $htmldiff, $unhide ); |
| 950 | + $de = new DifferenceEngine( $this->mTitle, $oldid, $diff, $rcid, $purge, $unhide ); |
952 | 951 | // DifferenceEngine directly fetched the revision: |
953 | 952 | $this->mRevIdFetched = $de->mNewid; |
954 | 953 | $de->showDiffPage( $diffOnly ); |
Index: trunk/phase3/languages/messages/MessagesEn.php |
— | — | @@ -1563,64 +1563,8 @@ |
1564 | 1564 | 'lineno' => 'Line $1:', |
1565 | 1565 | 'compareselectedversions' => 'Compare selected revisions', |
1566 | 1566 | 'showhideselectedversions' => 'Show/hide selected revisions', |
1567 | | -'visualcomparison' => 'Visual comparison', |
1568 | | -'wikicodecomparison' => 'Wikitext comparison', |
1569 | 1567 | 'editundo' => 'undo', |
1570 | 1568 | 'diff-multi' => '({{PLURAL:$1|One intermediate revision|$1 intermediate revisions}} not shown)', |
1571 | | -'diff-movedto' => 'moved to $1', |
1572 | | -'diff-styleadded' => '$1 style added', |
1573 | | -'diff-added' => '$1 added', |
1574 | | -'diff-changedto' => 'changed to $1', |
1575 | | -'diff-movedoutof' => 'moved out of $1', |
1576 | | -'diff-styleremoved' => '$1 style removed', |
1577 | | -'diff-removed' => '$1 removed', |
1578 | | -'diff-changedfrom' => 'changed from $1', |
1579 | | -'diff-src' => 'source', |
1580 | | -'diff-withdestination' => 'with destination $1', |
1581 | | -'diff-with' => ' with $1 $2', |
1582 | | -'diff-with-additional' => '$1 $2', # only translate this message to other languages if you have to change it |
1583 | | -'diff-with-final' => ' and $1 $2', |
1584 | | -'diff-width' => 'width', |
1585 | | -'diff-height' => 'height', |
1586 | | -'diff-p' => "a '''paragraph'''", |
1587 | | -'diff-blockquote' => "a '''quote'''", |
1588 | | -'diff-h1' => "a '''heading (level 1)'''", |
1589 | | -'diff-h2' => "a '''heading (level 2)'''", |
1590 | | -'diff-h3' => "a '''heading (level 3)'''", |
1591 | | -'diff-h4' => "a '''heading (level 4)'''", |
1592 | | -'diff-h5' => "a '''heading (level 5)'''", |
1593 | | -'diff-pre' => "a '''preformatted block'''", |
1594 | | -'diff-div' => "a '''division'''", |
1595 | | -'diff-ul' => "an '''unordered list'''", |
1596 | | -'diff-ol' => "an '''ordered list'''", |
1597 | | -'diff-li' => "a '''list item'''", |
1598 | | -'diff-table' => "a '''table'''", |
1599 | | -'diff-tbody' => "a '''table's content'''", |
1600 | | -'diff-tr' => "a '''row'''", |
1601 | | -'diff-td' => "a '''cell'''", |
1602 | | -'diff-th' => "a '''header'''", |
1603 | | -'diff-br' => "a '''break'''", |
1604 | | -'diff-hr' => "a '''horizontal rule'''", |
1605 | | -'diff-code' => "a '''computer code block'''", |
1606 | | -'diff-dl' => "a '''definition list'''", |
1607 | | -'diff-dt' => "a '''definition term'''", |
1608 | | -'diff-dd' => "a '''definition'''", |
1609 | | -'diff-input' => "an '''input'''", |
1610 | | -'diff-form' => "a '''form'''", |
1611 | | -'diff-img' => "an '''image'''", |
1612 | | -'diff-span' => "a '''span'''", |
1613 | | -'diff-a' => "a '''link'''", |
1614 | | -'diff-i' => "'''italics'''", |
1615 | | -'diff-b' => "'''bold'''", |
1616 | | -'diff-strong' => "'''strong'''", |
1617 | | -'diff-em' => "'''emphasis'''", |
1618 | | -'diff-font' => "'''font'''", |
1619 | | -'diff-big' => "'''big'''", |
1620 | | -'diff-del' => "'''deleted'''", |
1621 | | -'diff-tt' => "'''fixed width'''", |
1622 | | -'diff-sub' => "'''subscript'''", |
1623 | | -'diff-sup' => "'''superscript'''", |
1624 | | -'diff-strike' => "'''strikethrough'''", |
1625 | 1569 | |
1626 | 1570 | # Search results |
1627 | 1571 | 'searchresults' => 'Search results', |
— | — | @@ -3276,7 +3220,6 @@ |
3277 | 3221 | 'accesskey-preview' => 'p', # do not translate or duplicate this message to other languages |
3278 | 3222 | 'accesskey-diff' => 'v', # do not translate or duplicate this message to other languages |
3279 | 3223 | 'accesskey-compareselectedversions' => 'v', # do not translate or duplicate this message to other languages |
3280 | | -'accesskey-visualcomparison' => 'b', # do not translate or duplicate this message to other languages |
3281 | 3224 | 'accesskey-watch' => 'w', # do not translate or duplicate this message to other languages |
3282 | 3225 | 'accesskey-upload' => 's', # do not translate or duplicate this message to other languages |
3283 | 3226 | |
— | — | @@ -3475,9 +3418,6 @@ |
3476 | 3419 | 'previousdiff' => '← Older edit', |
3477 | 3420 | 'nextdiff' => 'Newer edit →', |
3478 | 3421 | |
3479 | | -# Visual comparison |
3480 | | -'visual-comparison' => 'Visual comparison', |
3481 | | - |
3482 | 3422 | # Media information |
3483 | 3423 | 'mediawarning' => "'''Warning''': This file may contain malicious code, by executing it your system may be compromised.<hr />", |
3484 | 3424 | 'imagemaxsize' => "Image size limit:<br />''(for file description pages)''", |
Index: trunk/phase3/RELEASE-NOTES |
— | — | @@ -89,6 +89,7 @@ |
90 | 90 | maintenance |
91 | 91 | * $wgCapitalLinkOverrides added to configure per-namespace capitalization |
92 | 92 | * (bug 21172) $wgSorbsUrl can now be an array with multiple DNSBL |
| 93 | +* $wgEnableHtmlDiff has been removed |
93 | 94 | |
94 | 95 | === New features in 1.16 === |
95 | 96 | |
— | — | @@ -604,6 +605,7 @@ |
605 | 606 | * User::isValidPassword now only returns boolean results, User::getPasswordValidity |
606 | 607 | can be used to get an error message string |
607 | 608 | * The error message shown in Special:ChangePassword now parses wiki markup |
| 609 | +* (bug 19859) Removed experimental HTMLDiff feature |
608 | 610 | |
609 | 611 | == API changes in 1.16 == |
610 | 612 | |
Index: trunk/phase3/maintenance/language/messages.inc |
— | — | @@ -766,64 +766,7 @@ |
767 | 767 | 'lineno', |
768 | 768 | 'compareselectedversions', |
769 | 769 | 'showhideselectedversions', |
770 | | - 'visualcomparison', |
771 | | - 'wikicodecomparison', |
772 | 770 | 'editundo', |
773 | | - 'diff-multi', |
774 | | - 'diff-movedto', |
775 | | - 'diff-styleadded', |
776 | | - 'diff-added', |
777 | | - 'diff-changedto', |
778 | | - 'diff-movedoutof', |
779 | | - 'diff-styleremoved', |
780 | | - 'diff-removed', |
781 | | - 'diff-changedfrom', |
782 | | - 'diff-src', |
783 | | - 'diff-withdestination', |
784 | | - 'diff-with', |
785 | | - 'diff-with-additional', |
786 | | - 'diff-with-final', |
787 | | - 'diff-width', |
788 | | - 'diff-height', |
789 | | - 'diff-p', |
790 | | - 'diff-blockquote', |
791 | | - 'diff-h1', |
792 | | - 'diff-h2', |
793 | | - 'diff-h3', |
794 | | - 'diff-h4', |
795 | | - 'diff-h5', |
796 | | - 'diff-pre', |
797 | | - 'diff-div', |
798 | | - 'diff-ul', |
799 | | - 'diff-ol', |
800 | | - 'diff-li', |
801 | | - 'diff-table', |
802 | | - 'diff-tbody', |
803 | | - 'diff-tr', |
804 | | - 'diff-td', |
805 | | - 'diff-th', |
806 | | - 'diff-br', |
807 | | - 'diff-hr', |
808 | | - 'diff-code', |
809 | | - 'diff-dl', |
810 | | - 'diff-dt', |
811 | | - 'diff-dd', |
812 | | - 'diff-input', |
813 | | - 'diff-form', |
814 | | - 'diff-img', |
815 | | - 'diff-span', |
816 | | - 'diff-a', |
817 | | - 'diff-i', |
818 | | - 'diff-b', |
819 | | - 'diff-strong', |
820 | | - 'diff-em', |
821 | | - 'diff-font', |
822 | | - 'diff-big', |
823 | | - 'diff-del', |
824 | | - 'diff-tt', |
825 | | - 'diff-sub', |
826 | | - 'diff-sup', |
827 | | - 'diff-strike', |
828 | 771 | ), |
829 | 772 | 'search' => array( |
830 | 773 | 'searchresults', |
— | — | @@ -2262,7 +2205,6 @@ |
2263 | 2206 | 'accesskey-preview', |
2264 | 2207 | 'accesskey-diff', |
2265 | 2208 | 'accesskey-compareselectedversions', |
2266 | | - 'accesskey-visualcomparison', |
2267 | 2209 | 'accesskey-watch', |
2268 | 2210 | 'accesskey-upload', |
2269 | 2211 | ), |
— | — | @@ -2454,9 +2396,6 @@ |
2455 | 2397 | 'previousdiff', |
2456 | 2398 | 'nextdiff', |
2457 | 2399 | ), |
2458 | | - 'visual-comparison' => array( |
2459 | | - 'visual-comparison', |
2460 | | - ), |
2461 | 2400 | 'media-info' => array( |
2462 | 2401 | 'mediawarning', |
2463 | 2402 | 'imagemaxsize', |
— | — | @@ -3304,7 +3243,6 @@ |
3305 | 3244 | 'variantname-kk' => 'Variants for Kazakh language', |
3306 | 3245 | 'variantname-ku' => 'Variants for Kurdish language', |
3307 | 3246 | 'variantname-tg' => 'Variants for Tajiki language', |
3308 | | - 'visual-comparison' => 'Visual comparison', |
3309 | 3247 | 'media-info' => 'Media information', |
3310 | 3248 | 'metadata' => 'Metadata', |
3311 | 3249 | 'exif' => 'EXIF tags', |
Index: trunk/phase3/maintenance/language/messageTypes.inc |
— | — | @@ -67,7 +67,6 @@ |
68 | 68 | 'accesskey-preview', |
69 | 69 | 'accesskey-diff', |
70 | 70 | 'accesskey-compareselectedversions', |
71 | | - 'accesskey-visualcomparison', |
72 | 71 | 'accesskey-watch', |
73 | 72 | 'accesskey-upload', |
74 | 73 | 'addsection', |