Index: includes/Article.php =================================================================== --- includes/Article.php (revision 53587) +++ includes/Article.php (working copy) @@ -922,11 +922,10 @@ $rcid = $wgRequest->getVal( 'rcid' ); $diffOnly = $wgRequest->getBool( 'diffonly', $wgUser->getOption( 'diffonly' ) ); $purge = $wgRequest->getVal( 'action' ) == 'purge'; - $htmldiff = $wgRequest->getVal( 'htmldiff' , false); $unhide = $wgRequest->getInt('unhide') == 1; $oldid = $this->getOldID(); - $de = new DifferenceEngine( $this->mTitle, $oldid, $diff, $rcid, $purge, $htmldiff, $unhide ); + $de = new DifferenceEngine( $this->mTitle, $oldid, $diff, $rcid, $purge, $unhide ); // DifferenceEngine directly fetched the revision: $this->mRevIdFetched = $de->mNewid; $de->showDiffPage( $diffOnly ); Index: includes/AutoLoader.php =================================================================== --- includes/AutoLoader.php (revision 53587) +++ includes/AutoLoader.php (working copy) @@ -369,13 +369,7 @@ 'SearchIBM_DB2' => 'includes/SearchIBM_DB2.php', # includes/diff - 'AncestorComparator' => 'includes/diff/HTMLDiff.php', - 'AnchorToString' => 'includes/diff/HTMLDiff.php', 'ArrayDiffFormatter' => 'includes/diff/DifferenceEngine.php', - 'BodyNode' => 'includes/diff/Nodes.php', - 'ChangeText' => 'includes/diff/HTMLDiff.php', - 'ChangeTextGenerator' => 'includes/diff/HTMLDiff.php', - 'DelegatingContentHandler' => 'includes/diff/HTMLDiff.php', '_DiffEngine' => 'includes/diff/DifferenceEngine.php', 'DifferenceEngine' => 'includes/diff/DifferenceEngine.php', 'DiffFormatter' => 'includes/diff/DifferenceEngine.php', @@ -385,27 +379,11 @@ '_DiffOp_Copy' => 'includes/diff/DifferenceEngine.php', '_DiffOp_Delete' => 'includes/diff/DifferenceEngine.php', '_DiffOp' => 'includes/diff/DifferenceEngine.php', - 'DomTreeBuilder' => 'includes/diff/HTMLDiff.php', - 'DummyNode' => 'includes/diff/Nodes.php', - 'HTMLDiffer' => 'includes/diff/HTMLDiff.php', - 'HTMLOutput' => 'includes/diff/HTMLDiff.php', '_HWLDF_WordAccumulator' => 'includes/diff/DifferenceEngine.php', - 'ImageNode' => 'includes/diff/Nodes.php', - 'LastCommonParentResult' => 'includes/diff/HTMLDiff.php', 'MappedDiff' => 'includes/diff/DifferenceEngine.php', - 'Modification' => 'includes/diff/HTMLDiff.php', - 'NoContentTagToString' => 'includes/diff/HTMLDiff.php', - 'Node' => 'includes/diff/Nodes.php', 'RangeDifference' => 'includes/diff/Diff.php', 'TableDiffFormatter' => 'includes/diff/DifferenceEngine.php', - 'TagNode' => 'includes/diff/Nodes.php', - 'TagToString' => 'includes/diff/HTMLDiff.php', - 'TagToStringFactory' => 'includes/diff/HTMLDiff.php', - 'TextNode' => 'includes/diff/Nodes.php', - 'TextNodeDiffer' => 'includes/diff/HTMLDiff.php', - 'TextOnlyComparator' => 'includes/diff/HTMLDiff.php', 'UnifiedDiffFormatter' => 'includes/diff/DifferenceEngine.php', - 'WhiteSpaceNode' => 'includes/diff/Nodes.php', 'WikiDiff3' => 'includes/diff/Diff.php', 'WordLevelDiff' => 'includes/diff/DifferenceEngine.php', Index: includes/DefaultSettings.php =================================================================== --- includes/DefaultSettings.php (revision 53587) +++ includes/DefaultSettings.php (working copy) @@ -2802,9 +2802,6 @@ /** Name of the external diff engine to use */ $wgExternalDiffEngine = false; -/** Whether to use inline diff */ -$wgEnableHtmlDiff = false; - /** Use RC Patrolling to check for vandalism */ $wgUseRCPatrol = true; Index: includes/diff/DifferenceEngine.php =================================================================== --- includes/diff/DifferenceEngine.php (revision 53587) +++ includes/diff/DifferenceEngine.php (working copy) @@ -40,11 +40,10 @@ * @param $new String: either 'prev' or 'next'. * @param $rcid Integer: ??? FIXME (default 0) * @param $refreshCache boolean If set, refreshes the diff cache - * @param $htmldiff boolean If set, output using HTMLDiff instead of raw wikicode diff * @param $unhide boolean If set, allow viewing deleted revs */ function __construct( $titleObj = null, $old = 0, $new = 0, $rcid = 0, - $refreshCache = false, $htmldiff = false, $unhide = false ) + $refreshCache = false, $unhide = false ) { if ( $titleObj ) { $this->mTitle = $titleObj; @@ -249,12 +248,6 @@ $query['diffonly'] = $diffOnly; } - $htmldiffarg = $this->htmlDiffArgument(); - - if( $htmldiffarg ) { - $query['htmldiff'] = $htmldiffarg['htmldiff']; - } - # Make "previous revision link" $query['diff'] = 'prev'; $query['oldid'] = $this->mOldid; @@ -363,45 +356,7 @@ $wgOut->wrapWikiMsg( "\n", array( 'rev-deleted-unhide-diff', $link ) ); } - } else if( $wgEnableHtmlDiff && $this->htmldiff ) { - $multi = $this->getMultiNotice(); - $wgOut->addHTML( '
' . $sk->link( - $this->mTitle, - wfMsgHtml( 'wikicodecomparison' ), - array( - 'id' => 'differences-switchtype' - ), - array( - 'diff' => $this->mNewid, - 'oldid' => $this->mOldid, - 'htmldiff' => 0 - ), - array( - 'known', - 'noclasses' - ) - ) . '
'); - $wgOut->addHTML( $this->addHeader( '', $oldHeader, $newHeader, $multi ) ); - $this->renderHtmlDiff(); } else { - if( $wgEnableHtmlDiff ) { - $wgOut->addHTML( '
' . $sk->link( - $this->mTitle, - wfMsgHtml( 'visualcomparison' ), - array( - 'id' => 'differences-switchtype' - ), - array( - 'diff' => $this->mNewid, - 'oldid' => $this->mOldid, - 'htmldiff' => 1 - ), - array( - 'known', - 'noclasses' - ) - ) . '
'); - } $this->showDiff( $oldHeader, $newHeader ); if( !$diffOnly ) { $this->renderNewRevision(); @@ -471,70 +426,6 @@ wfProfileOut( __METHOD__ ); } - - function renderHtmlDiff() { - global $wgOut, $wgParser, $wgDebugComments; - wfProfileIn( __METHOD__ ); - - $this->showDiffStyle(); - - $wgOut->addHTML( '

'.wfMsgHtml( 'visual-comparison' )."

\n" ); - #add deleted rev tag if needed - if( !$this->mNewRev->userCan(Revision::DELETED_TEXT) ) { - $wgOut->wrapWikiMsg( "\n", 'rev-deleted-text-permission' ); - } else if( $this->mNewRev->isDeleted(Revision::DELETED_TEXT) ) { - $wgOut->wrapWikiMsg( "\n", 'rev-deleted-text-view' ); - } - - if( !$this->mNewRev->isCurrent() ) { - $oldEditSectionSetting = $wgOut->parserOptions()->setEditSection( false ); - } - - $this->loadText(); - - // Old revision - if( is_object( $this->mOldRev ) ) { - $wgOut->setRevisionId( $this->mOldRev->getId() ); - } - - $popts = $wgOut->parserOptions(); - $oldTidy = $popts->setTidy( true ); - $popts->setEditSection( false ); - - $parserOutput = $wgParser->parse( $this->mOldtext, $this->getTitle(), $popts, true, true, $wgOut->getRevisionId() ); - $popts->setTidy( $oldTidy ); - - //only for new? - //$wgOut->addParserOutputNoText( $parserOutput ); - $oldHtml = $parserOutput->getText(); - wfRunHooks( 'OutputPageBeforeHTML', array( &$wgOut, &$oldHtml ) ); - - // New revision - if( is_object( $this->mNewRev ) ) { - $wgOut->setRevisionId( $this->mNewRev->getId() ); - } - - $popts = $wgOut->parserOptions(); - $oldTidy = $popts->setTidy( true ); - - $parserOutput = $wgParser->parse( $this->mNewtext, $this->getTitle(), $popts, true, true, $wgOut->getRevisionId() ); - $popts->setTidy( $oldTidy ); - - $wgOut->addParserOutputNoText( $parserOutput ); - $newHtml = $parserOutput->getText(); - wfRunHooks( 'OutputPageBeforeHTML', array( &$wgOut, &$newHtml ) ); - - unset($parserOutput, $popts); - - $differ = new HTMLDiffer(new DelegatingContentHandler($wgOut)); - $differ->htmlDiff($oldHtml, $newHtml); - if ( $wgDebugComments ) { - $wgOut->addHTML( "\n" ); - } - - wfProfileOut( __METHOD__ ); - } - /** * Show the first revision of an article. Uses normal diff headers in * contrast to normal "old revision" display style. @@ -583,7 +474,6 @@ array( 'diff' => 'next', 'oldid' => $this->mNewid, - $this->htmlDiffArgument() ), array( 'known', @@ -602,19 +492,6 @@ wfProfileOut( __METHOD__ ); } - function htmlDiffArgument(){ - global $wgEnableHtmlDiff; - if($wgEnableHtmlDiff){ - if($this->htmldiff){ - return array( 'htmldiff' => 1 ); - }else{ - return array( 'htmldiff' => 0 ); - } - }else{ - return array(); - } - } - /** * Get the diff text, send it to $wgOut * Returns false if the diff could not be generated, otherwise returns true Index: includes/diff/HTMLDiff.php =================================================================== --- includes/diff/HTMLDiff.php (revision 53587) +++ includes/diff/HTMLDiff.php (working copy) @@ -1,1009 +0,0 @@ - - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * or see http://www.gnu.org/ - * - * @ingroup DifferenceEngine - */ - -/** - * When detecting the last common parent of two nodes, all results are stored as - * a LastCommonParentResult. - */ -class LastCommonParentResult { - - // Parent - public $parent; - - // Splitting - public $splittingNeeded = false; - - // Depth - public $lastCommonParentDepth = -1; - - // Index - public $indexInLastCommonParent = -1; -} - -class Modification{ - - const NONE = 1; - const REMOVED = 2; - const ADDED = 4; - const CHANGED = 8; - - public $type; - - public $id = -1; - - public $firstOfID = false; - - public $changes; - - function __construct($type) { - $this->type = $type; - } - - public static function typeToString($type) { - switch($type) { - case self::NONE: return 'none'; - case self::REMOVED: return 'removed'; - case self::ADDED: return 'added'; - case self::CHANGED: return 'changed'; - } - } -} - -class DomTreeBuilder { - - public $textNodes = array(); - - public $bodyNode; - - private $currentParent; - - private $newWord = ''; - - protected $bodyStarted = false; - - protected $bodyEnded = false; - - private $whiteSpaceBeforeThis = false; - - private $lastSibling; - - private $notInPre = true; - - function __construct() { - $this->bodyNode = $this->currentParent = new BodyNode(); - $this->lastSibling = new DummyNode(); - } - - /** - * Must be called manually - */ - public function endDocument() { - $this->endWord(); - HTMLDiffer::diffDebug( count($this->textNodes) . " text nodes in document.\n" ); - } - - public function startElement($parser, $name, /*array*/ $attributes) { - if (strcasecmp($name, 'body') != 0) { - HTMLDiffer::diffDebug( "Starting $name node.\n" ); - $this->endWord(); - - $newNode = new TagNode($this->currentParent, $name, $attributes); - $this->currentParent->children[] = $newNode; - $this->currentParent = $newNode; - $this->lastSibling = new DummyNode(); - if ($this->whiteSpaceBeforeThis && !in_array(strtolower($this->currentParent->qName),TagNode::$blocks)) { - $this->currentParent->whiteBefore = true; - } - $this->whiteSpaceBeforeThis = false; - if(strcasecmp($name, 'pre') == 0) { - $this->notInPre = false; - } - } - } - - public function endElement($parser, $name) { - if(strcasecmp($name, 'body') != 0) { - HTMLDiffer::diffDebug( "Ending $name node.\n"); - if (0 == strcasecmp($name,'img')) { - // Insert a dummy leaf for the image - $img = new ImageNode($this->currentParent, $this->currentParent->attributes); - $this->currentParent->children[] = $img; - $img->whiteBefore = $this->whiteSpaceBeforeThis; - $this->lastSibling = $img; - $this->textNodes[] = $img; - } - $this->endWord(); - if (!in_array(strtolower($this->currentParent->qName),TagNode::$blocks)) { - $this->lastSibling = $this->currentParent; - } else { - $this->lastSibling = new DummyNode(); - } - $this->currentParent = $this->currentParent->parent; - $this->whiteSpaceBeforeThis = false; - if (!$this->notInPre && strcasecmp($name, 'pre') == 0) { - $this->notInPre = true; - } - } else { - $this->endDocument(); - } - } - - const regex = '/([\s\.\,\"\\\'\(\)\?\:\;\!\{\}\-\+\*\=\_\[\]\&\|\$]{1})/'; - const whitespace = '/^[\s]{1}$/'; - const delimiter = '/^[\s\.\,\"\\\'\(\)\?\:\;\!\{\}\-\+\*\=\_\[\]\&\|\$]{1}$/'; - - public function characters($parser, $data) { - $matches = preg_split(self::regex, $data, -1, PREG_SPLIT_DELIM_CAPTURE); - - foreach($matches as &$word) { - if (preg_match(self::whitespace, $word) && $this->notInPre) { - $this->endWord(); - $this->lastSibling->whiteAfter = true; - $this->whiteSpaceBeforeThis = true; - } else if (preg_match(self::delimiter, $word)) { - $this->endWord(); - $textNode = new TextNode($this->currentParent, $word); - $this->currentParent->children[] = $textNode; - $textNode->whiteBefore = $this->whiteSpaceBeforeThis; - $this->whiteSpaceBeforeThis = false; - $this->lastSibling = $textNode; - $this->textNodes[] = $textNode; - } else { - $this->newWord .= $word; - } - } - } - - private function endWord() { - if ($this->newWord !== '') { - $node = new TextNode($this->currentParent, $this->newWord); - $this->currentParent->children[] = $node; - $node->whiteBefore = $this->whiteSpaceBeforeThis; - $this->whiteSpaceBeforeThis = false; - $this->lastSibling = $node; - $this->textNodes[] = $node; - $this->newWord = ""; - } - } - - public function getDiffLines() { - return array_map(array('TextNode','toDiffLine'), $this->textNodes); - } -} - -class TextNodeDiffer { - - private $textNodes; - public $bodyNode; - - private $oldTextNodes; - private $oldBodyNode; - - private $newID = 0; - - private $changedID = 0; - - private $changedIDUsed = false; - - // used to remove the whitespace between a red and green block - private $whiteAfterLastChangedPart = false; - - private $deletedID = 0; - - function __construct(DomTreeBuilder $tree, DomTreeBuilder $oldTree) { - $this->textNodes = $tree->textNodes; - $this->bodyNode = $tree->bodyNode; - $this->oldTextNodes = $oldTree->textNodes; - $this->oldBodyNode = $oldTree->bodyNode; - } - - public function markAsNew($start, $end) { - if ($end <= $start) { - return; - } - - if ($this->whiteAfterLastChangedPart) { - $this->textNodes[$start]->whiteBefore = false; - } - - for ($i = $start; $i < $end; ++$i) { - $mod = new Modification(Modification::ADDED); - $mod->id = $this->newID; - $this->textNodes[$i]->modification = $mod; - } - if ($start < $end) { - $this->textNodes[$start]->modification->firstOfID = true; - } - ++$this->newID; - } - - public function handlePossibleChangedPart($leftstart, $leftend, $rightstart, $rightend) { - $i = $rightstart; - $j = $leftstart; - - if ($this->changedIDUsed) { - ++$this->changedID; - $this->changedIDUsed = false; - } - - $changes; - while ($i < $rightend) { - $acthis = new AncestorComparator($this->textNodes[$i]->getParentTree()); - $acother = new AncestorComparator($this->oldTextNodes[$j]->getParentTree()); - $result = $acthis->getResult($acother); - unset($acthis, $acother); - - if ( $result ) { - $mod = new Modification(Modification::CHANGED); - - if (!$this->changedIDUsed) { - $mod->firstOfID = true; - } else if (!is_null( $result ) && $result !== $this->changes) { - ++$this->changedID; - $mod->firstOfID = true; - } - - $mod->changes = $result; - $mod->id = $this->changedID; - - $this->textNodes[$i]->modification = $mod; - $this->changes = $result; - $this->changedIDUsed = true; - } else if ($this->changedIDUsed) { - ++$this->changedID; - $this->changedIDUsed = false; - } - ++$i; - ++$j; - } - } - - public function markAsDeleted($start, $end, $before) { - - if ($end <= $start) { - return; - } - - if ($before > 0 && $this->textNodes[$before - 1]->whiteAfter) { - $this->whiteAfterLastChangedPart = true; - } else { - $this->whiteAfterLastChangedPart = false; - } - - for ($i = $start; $i < $end; ++$i) { - $mod = new Modification(Modification::REMOVED); - $mod->id = $this->deletedID; - - // oldTextNodes is used here because we're going to move its deleted - // elements to this tree! - $this->oldTextNodes[$i]->modification = $mod; - } - $this->oldTextNodes[$start]->modification->firstOfID = true; - - $root = $this->oldTextNodes[$start]->getLastCommonParent($this->oldTextNodes[$end-1])->parent; - - $junk1 = $junk2 = null; - $deletedNodes = $root->getMinimalDeletedSet($this->deletedID, $junk1, $junk2); - - HTMLDiffer::diffDebug( "Minimal set of deleted nodes of size " . count($deletedNodes) . "\n" ); - - // Set prevLeaf to the leaf after which the old HTML needs to be - // inserted - if ($before > 0) { - $prevLeaf = $this->textNodes[$before - 1]; - } - // Set nextLeaf to the leaf before which the old HTML needs to be - // inserted - if ($before < count($this->textNodes)) { - $nextLeaf = $this->textNodes[$before]; - } - - while (count($deletedNodes) > 0) { - if (isset($prevLeaf)) { - $prevResult = $prevLeaf->getLastCommonParent($deletedNodes[0]); - } else { - $prevResult = new LastCommonParentResult(); - $prevResult->parent = $this->bodyNode; - $prevResult->indexInLastCommonParent = -1; - } - if (isset($nextleaf)) { - $nextResult = $nextLeaf->getLastCommonParent($deletedNodes[count($deletedNodes) - 1]); - } else { - $nextResult = new LastCommonParentResult(); - $nextResult->parent = $this->bodyNode; - $nextResult->indexInLastCommonParent = $this->bodyNode->getNbChildren(); - } - - if ($prevResult->lastCommonParentDepth == $nextResult->lastCommonParentDepth) { - // We need some metric to choose which way to add-... - if ($deletedNodes[0]->parent === $deletedNodes[count($deletedNodes) - 1]->parent - && $prevResult->parent === $nextResult->parent) { - // The difference is not in the parent - $prevResult->lastCommonParentDepth = $prevResult->lastCommonParentDepth + 1; - } else { - // The difference is in the parent, so compare them - // now THIS is tricky - $distancePrev = $deletedNodes[0]->parent->getMatchRatio($prevResult->parent); - $distanceNext = $deletedNodes[count($deletedNodes) - 1]->parent->getMatchRatio($nextResult->parent); - - if ($distancePrev <= $distanceNext) { - $prevResult->lastCommonParentDepth = $prevResult->lastCommonParentDepth + 1; - } else { - $nextResult->lastCommonParentDepth = $nextResult->lastCommonParentDepth + 1; - } - } - - } - - if ($prevResult->lastCommonParentDepth > $nextResult->lastCommonParentDepth) { - // Inserting at the front - if ($prevResult->splittingNeeded) { - $prevLeaf->parent->splitUntil($prevResult->parent, $prevLeaf, true); - } - $prevLeaf = $deletedNodes[0]->copyTree(); - unset($deletedNodes[0]); - $deletedNodes = array_values($deletedNodes); - $prevLeaf->setParent($prevResult->parent); - $prevResult->parent->addChildAbsolute($prevLeaf,$prevResult->indexInLastCommonParent + 1); - } else if ($prevResult->lastCommonParentDepth < $nextResult->lastCommonParentDepth) { - // Inserting at the back - if ($nextResult->splittingNeeded) { - $splitOccured = $nextLeaf->parent->splitUntil($nextResult->parent, $nextLeaf, false); - if ($splitOccured) { - // The place where to insert is shifted one place to the - // right - $nextResult->indexInLastCommonParent = $nextResult->indexInLastCommonParent + 1; - } - } - $nextLeaf = $deletedNodes[count(deletedNodes) - 1]->copyTree(); - unset($deletedNodes[count(deletedNodes) - 1]); - $deletedNodes = array_values($deletedNodes); - $nextLeaf->setParent($nextResult->parent); - $nextResult->parent->addChildAbsolute($nextLeaf,$nextResult->indexInLastCommonParent); - } - } - ++$this->deletedID; - } - - public function expandWhiteSpace() { - $this->bodyNode->expandWhiteSpace(); - } - - public function lengthNew(){ - return count($this->textNodes); - } - - public function lengthOld(){ - return count($this->oldTextNodes); - } -} - -class HTMLDiffer { - - private $output; - private static $debug = ''; - - function __construct($output) { - $this->output = $output; - } - - function htmlDiff($from, $to) { - wfProfileIn( __METHOD__ ); - // Create an XML parser - $xml_parser = xml_parser_create(''); - - $domfrom = new DomTreeBuilder(); - - // Set the functions to handle opening and closing tags - xml_set_element_handler($xml_parser, array($domfrom, "startElement"), array($domfrom, "endElement")); - - // Set the function to handle blocks of character data - xml_set_character_data_handler($xml_parser, array($domfrom, "characters")); - - HTMLDiffer::diffDebug( "Parsing " . strlen($from) . " characters worth of HTML\n" ); - if (!xml_parse($xml_parser, ''.Sanitizer::hackDocType().'', false) - || !xml_parse($xml_parser, $from, false) - || !xml_parse($xml_parser, '', true)){ - $error = xml_error_string(xml_get_error_code($xml_parser)); - $line = xml_get_current_line_number($xml_parser); - HTMLDiffer::diffDebug( "XML error: $error at line $line\n" ); - } - xml_parser_free($xml_parser); - unset($from); - - $xml_parser = xml_parser_create(''); - - $domto = new DomTreeBuilder(); - - // Set the functions to handle opening and closing tags - xml_set_element_handler($xml_parser, array($domto, "startElement"), array($domto, "endElement")); - - // Set the function to handle blocks of character data - xml_set_character_data_handler($xml_parser, array($domto, "characters")); - - HTMLDiffer::diffDebug( "Parsing " . strlen($to) . " characters worth of HTML\n" ); - if (!xml_parse($xml_parser, ''.Sanitizer::hackDocType().'', false) - || !xml_parse($xml_parser, $to, false) - || !xml_parse($xml_parser, '', true)){ - $error = xml_error_string(xml_get_error_code($xml_parser)); - $line = xml_get_current_line_number($xml_parser); - HTMLDiffer::diffDebug( "XML error: $error at line $line\n" ); - } - xml_parser_free($xml_parser); - unset($to); - - $diffengine = new WikiDiff3(); - $differences = $this->preProcess($diffengine->diff_range($domfrom->getDiffLines(), $domto->getDiffLines())); - unset($xml_parser, $diffengine); - - $domdiffer = new TextNodeDiffer($domto, $domfrom); - - $currentIndexLeft = 0; - $currentIndexRight = 0; - foreach ($differences as &$d) { - if ($d->leftstart > $currentIndexLeft) { - $domdiffer->handlePossibleChangedPart($currentIndexLeft, $d->leftstart, - $currentIndexRight, $d->rightstart); - } - if ($d->leftlength > 0) { - $domdiffer->markAsDeleted($d->leftstart, $d->leftend, $d->rightstart); - } - $domdiffer->markAsNew($d->rightstart, $d->rightend); - - $currentIndexLeft = $d->leftend; - $currentIndexRight = $d->rightend; - } - $oldLength = $domdiffer->lengthOld(); - if ($currentIndexLeft < $oldLength) { - $domdiffer->handlePossibleChangedPart($currentIndexLeft, $oldLength, $currentIndexRight, $domdiffer->lengthNew()); - } - $domdiffer->expandWhiteSpace(); - $output = new HTMLOutput('htmldiff', $this->output); - $output->parse($domdiffer->bodyNode); - wfProfileOut( __METHOD__ ); - } - - private function preProcess(/*array*/ $differences) { - $newRanges = array(); - - $nbDifferences = count($differences); - for ($i = 0; $i < $nbDifferences; ++$i) { - $leftStart = $differences[$i]->leftstart; - $leftEnd = $differences[$i]->leftend; - $rightStart = $differences[$i]->rightstart; - $rightEnd = $differences[$i]->rightend; - - $leftLength = $leftEnd - $leftStart; - $rightLength = $rightEnd - $rightStart; - - while ($i + 1 < $nbDifferences && self::score($leftLength, - $differences[$i + 1]->leftlength, - $rightLength, - $differences[$i + 1]->rightlength) - > ($differences[$i + 1]->leftstart - $leftEnd)) { - $leftEnd = $differences[$i + 1]->leftend; - $rightEnd = $differences[$i + 1]->rightend; - $leftLength = $leftEnd - $leftStart; - $rightLength = $rightEnd - $rightStart; - ++$i; - } - $newRanges[] = new RangeDifference($leftStart, $leftEnd, $rightStart, $rightEnd); - } - return $newRanges; - } - - /** - * Heuristic to merge differences for readability. - */ - public static function score($ll, $nll, $rl, $nrl) { - if (($ll == 0 && $nll == 0) - || ($rl == 0 && $nrl == 0)) { - return 0; - } - $numbers = array($ll, $nll, $rl, $nrl); - $d = 0; - foreach ($numbers as &$number) { - while ($number > 3) { - $d += 3; - $number -= 3; - $number *= 0.5; - } - $d += $number; - - } - return $d / (1.5 * count($numbers)); - } - - /** - * Add to debug output - * @param string $str Debug output - */ - public static function diffDebug( $str ) { - self :: $debug .= $str; - } - - /** - * Get debug output - * @return string - */ - public static function getDebugOutput() { - return self :: $debug; - } - -} - -class TextOnlyComparator { - - public $leafs = array(); - - function _construct(TagNode $tree) { - $this->addRecursive($tree); - $this->leafs = array_map(array('TextNode','toDiffLine'), $this->leafs); - } - - private function addRecursive(TagNode $tree) { - foreach ($tree->children as &$child) { - if ($child instanceof TagNode) { - $this->addRecursive($child); - } else if ($child instanceof TextNode) { - $this->leafs[] = $node; - } - } - } - - public function getMatchRatio(TextOnlyComparator $other) { - $nbOthers = count($other->leafs); - $nbThis = count($this->leafs); - if($nbOthers == 0 || $nbThis == 0){ - return -log(0); - } - - $diffengine = new WikiDiff3(25000, 1.35); - $diffengine->diff($this->leafs, $other->leafs); - - $lcsLength = $diffengine->getLcsLength(); - - $distanceThis = $nbThis-$lcsLength; - - return (2.0 - $lcsLength/$nbOthers - $lcsLength/$nbThis) / 2.0; - } -} - -/** - * A comparator used when calculating the difference in ancestry of two Nodes. - */ -class AncestorComparator { - - public $ancestors; - public $ancestorsText; - - function __construct(/*array*/ $ancestors) { - $this->ancestors = $ancestors; - $this->ancestorsText = array_map(array('TagNode','toDiffLine'), $ancestors); - } - - public $compareTxt = ""; - - public function getResult(AncestorComparator $other) { - - $diffengine = new WikiDiff3(10000, 1.35); - $differences = $diffengine->diff_range($other->ancestorsText,$this->ancestorsText); - - if (count($differences) == 0){ - return null; - } - $changeTxt = new ChangeTextGenerator($this, $other); - - return $changeTxt->getChanged($differences)->toString();; - } -} - -class ChangeTextGenerator { - - private $ancestorComparator; - private $other; - - private $factory; - - function __construct(AncestorComparator $ancestorComparator, AncestorComparator $other) { - $this->ancestorComparator = $ancestorComparator; - $this->other = $other; - $this->factory = new TagToStringFactory(); - } - - public function getChanged(/*array*/ $differences) { - $txt = new ChangeText; - $rootlistopened = false; - if (count($differences) > 1) { - $txt->addHtml(''); - } - return $txt; - } - - private function addTagOld(ChangeText $txt, TagNode $ancestor) { - $this->factory->create($ancestor)->getRemovedDescription($txt); - } - - private function addTagNew(ChangeText $txt, TagNode $ancestor) { - $this->factory->create($ancestor)->getAddedDescription($txt); - } -} - -class ChangeText { - - private $txt = ""; - - public function addHtml($s) { - $this->txt .= $s; - } - - public function toString() { - return $this->txt; - } -} - -class TagToStringFactory { - - private static $containerTags = array('html', 'body', 'p', 'blockquote', - 'h1', 'h2', 'h3', 'h4', 'h5', 'pre', 'div', 'ul', 'ol', 'li', - 'table', 'tbody', 'tr', 'td', 'th', 'br', 'hr', 'code', 'dl', - 'dt', 'dd', 'input', 'form', 'img', 'span', 'a'); - - private static $styleTags = array('i', 'b', 'strong', 'em', 'font', - 'big', 'del', 'tt', 'sub', 'sup', 'strike'); - - const MOVED = 1; - const STYLE = 2; - const UNKNOWN = 4; - - public function create(TagNode $node) { - $sem = $this->getChangeSemantic($node->qName); - if (strcasecmp($node->qName,'a') == 0) { - return new AnchorToString($node, $sem); - } - if (strcasecmp($node->qName,'img') == 0) { - return new NoContentTagToString($node, $sem); - } - return new TagToString($node, $sem); - } - - protected function getChangeSemantic($qname) { - if (in_array(strtolower($qname),self::$containerTags)) { - return self::MOVED; - } - if (in_array(strtolower($qname),self::$styleTags)) { - return self::STYLE; - } - return self::UNKNOWN; - } -} - -class TagToString { - - protected $node; - - protected $sem; - - function __construct(TagNode $node, $sem) { - $this->node = $node; - $this->sem = $sem; - } - - public function getRemovedDescription(ChangeText $txt) { - $tagDescription = wfMsgExt('diff-' . $this->node->qName, 'parseinline' ); - if( wfEmptyMsg( 'diff-' . $this->node->qName, $tagDescription ) ){ - $tagDescription = "<" . $this->node->qName . ">"; - } - if ($this->sem == TagToStringFactory::MOVED) { - $txt->addHtml( wfMsgExt( 'diff-movedoutof', 'parseinline', $tagDescription ) ); - } else if ($this->sem == TagToStringFactory::STYLE) { - $txt->addHtml( wfMsgExt( 'diff-styleremoved' , 'parseinline', $tagDescription ) ); - } else { - $txt->addHtml( wfMsgExt( 'diff-removed' , 'parseinline', $tagDescription ) ); - } - $this->addAttributes($txt, $this->node->attributes); - $txt->addHtml('.'); - } - - public function getAddedDescription(ChangeText $txt) { - $tagDescription = wfMsgExt('diff-' . $this->node->qName, 'parseinline' ); - if( wfEmptyMsg( 'diff-' . $this->node->qName, $tagDescription ) ){ - $tagDescription = "<" . $this->node->qName . ">"; - } - if ($this->sem == TagToStringFactory::MOVED) { - $txt->addHtml( wfMsgExt( 'diff-movedto' , 'parseinline', $tagDescription) ); - } else if ($this->sem == TagToStringFactory::STYLE) { - $txt->addHtml( wfMsgExt( 'diff-styleadded', 'parseinline', $tagDescription ) ); - } else { - $txt->addHtml( wfMsgExt( 'diff-added', 'parseinline', $tagDescription ) ); - } - $this->addAttributes($txt, $this->node->attributes); - $txt->addHtml('.'); - } - - protected function addAttributes(ChangeText $txt, array $attributes) { - if (count($attributes) < 1) { - return; - } - $firstOne = true; - $nbAttributes_min_1 = count($attributes)-1; - $keys = array_keys($attributes); - for ($i=0;$i<$nbAttributes_min_1;$i++) { - $key = $keys[$i]; - $attr = $attributes[$key]; - if($firstOne) { - $firstOne = false; - $txt->addHtml( wfMsgExt('diff-with', 'escapenoentities', $this->translateArgument($key), htmlspecialchars($attr) ) ); - continue; - } - $txt->addHtml( wfMsgExt( 'comma-separator', 'escapenoentities' ) . - wfMsgExt( 'diff-with-additional', 'escapenoentities', - $this->translateArgument( $key ), htmlspecialchars( $attr ) ) - ); - } - - if ($nbAttributes_min_1 > 0) { - $txt->addHtml( wfMsgExt( 'diff-with-final', 'escapenoentities', - $this->translateArgument($keys[$nbAttributes_min_1]), - htmlspecialchars($attributes[$keys[$nbAttributes_min_1]]) ) ); - } - } - - protected function translateArgument($name) { - $translation = wfMsgExt('diff-' . $name, 'parseinline' ); - if ( wfEmptyMsg( 'diff-' . $name, $translation ) ) { - $translation = "<" . $name . ">";; - } - return htmlspecialchars( $translation ); - } -} - -class NoContentTagToString extends TagToString { - - function __construct(TagNode $node, $sem) { - parent::__construct($node, $sem); - } - - public function getAddedDescription(ChangeText $txt) { - $tagDescription = wfMsgExt('diff-' . $this->node->qName, 'parseinline' ); - if( wfEmptyMsg( 'diff-' . $this->node->qName, $tagDescription ) ){ - $tagDescription = "<" . $this->node->qName . ">"; - } - $txt->addHtml( wfMsgExt('diff-changedto', 'parseinline', $tagDescription ) ); - $this->addAttributes($txt, $this->node->attributes); - $txt->addHtml('.'); - } - - public function getRemovedDescription(ChangeText $txt) { - $tagDescription = wfMsgExt('diff-' . $this->node->qName, 'parseinline' ); - if( wfEmptyMsg( 'diff-' . $this->node->qName, $tagDescription ) ){ - $tagDescription = "<" . $this->node->qName . ">"; - } - $txt->addHtml( wfMsgExt('diff-changedfrom', 'parseinline', $tagDescription ) ); - $this->addAttributes($txt, $this->node->attributes); - $txt->addHtml('.'); - } -} - -class AnchorToString extends TagToString { - - function __construct(TagNode $node, $sem) { - parent::__construct($node, $sem); - } - - protected function addAttributes(ChangeText $txt, array $attributes) { - if (array_key_exists('href', $attributes)) { - $txt->addHtml(' ' . wfMsgExt( 'diff-withdestination', 'parseinline', htmlspecialchars($attributes['href']) ) ); - unset($attributes['href']); - } - parent::addAttributes($txt, $attributes); - } -} - -/** - * Takes a branch root and creates an HTML file for it. - */ -class HTMLOutput{ - - private $prefix; - private $handler; - - function __construct($prefix, $handler) { - $this->prefix = $prefix; - $this->handler = $handler; - } - - public function parse(TagNode $node) { - $handler = &$this->handler; - - if (strcasecmp($node->qName, 'img') != 0 && strcasecmp($node->qName, 'body') != 0) { - $handler->startElement($node->qName, $node->attributes); - } - - $newStarted = false; - $remStarted = false; - $changeStarted = false; - $changeTXT = ''; - - foreach ($node->children as &$child) { - if ($child instanceof TagNode) { - if ($newStarted) { - $handler->endElement('span'); - $newStarted = false; - } else if ($changeStarted) { - $handler->endElement('span'); - $changeStarted = false; - } else if ($remStarted) { - $handler->endElement('span'); - $remStarted = false; - } - $this->parse($child); - } else if ($child instanceof TextNode) { - $mod = $child->modification; - - if ($newStarted && ($mod->type != Modification::ADDED || $mod->firstOfID)) { - $handler->endElement('span'); - $newStarted = false; - } else if ($changeStarted && ($mod->type != Modification::CHANGED - || $mod->changes != $changeTXT || $mod->firstOfID)) { - $handler->endElement('span'); - $changeStarted = false; - } else if ($remStarted && ($mod->type != Modification::REMOVED || $mod ->firstOfID)) { - $handler->endElement('span'); - $remStarted = false; - } - - // no else because a removed part can just be closed and a new - // part can start - if (!$newStarted && $mod->type == Modification::ADDED) { - $attrs = array('class' => 'diff-html-added'); - if ($mod->firstOfID) { - $attrs['id'] = "added-{$this->prefix}-{$mod->id}"; - } - $handler->startElement('span', $attrs); - $newStarted = true; - } else if (!$changeStarted && $mod->type == Modification::CHANGED) { - $attrs = array('class' => 'diff-html-changed'); - if ($mod->firstOfID) { - $attrs['id'] = "changed-{$this->prefix}-{$mod->id}"; - } - $handler->startElement('span', $attrs); - - //tooltip - $handler->startElement('span', array('class' => 'tip')); - $handler->html($mod->changes); - $handler->endElement('span'); - - $changeStarted = true; - $changeTXT = $mod->changes; - } else if (!$remStarted && $mod->type == Modification::REMOVED) { - $attrs = array('class'=>'diff-html-removed'); - if ($mod->firstOfID) { - $attrs['id'] = "removed-{$this->prefix}-{$mod->id}"; - } - $handler->startElement('span', $attrs); - $remStarted = true; - } - - $chars = $child->text; - - if ($child instanceof ImageNode) { - $this->writeImage($child); - } else { - $handler->characters($chars); - } - } - } - - if ($newStarted) { - $handler->endElement('span'); - $newStarted = false; - } else if ($changeStarted) { - $handler->endElement('span'); - $changeStarted = false; - } else if ($remStarted) { - $handler->endElement('span'); - $remStarted = false; - } - - if (strcasecmp($node->qName, 'img') != 0 - && strcasecmp($node->qName, 'body') != 0) { - $handler->endElement($node->qName); - } - } - - private function writeImage(ImageNode $imgNode) { - $attrs = $imgNode->attributes; - $this->handler->startElement('img', $attrs); - $this->handler->endElement('img'); - } -} - -class DelegatingContentHandler { - - private $delegate; - - function __construct($delegate) { - $this->delegate = $delegate; - } - - function startElement($qname, /*array*/ $arguments) { - $this->delegate->addHtml(Xml::openElement($qname, $arguments)); - } - - function endElement($qname){ - $this->delegate->addHtml(Xml::closeElement($qname)); - } - - function characters($chars){ - $this->delegate->addHtml(htmlspecialchars($chars)); - } - - function html($html){ - $this->delegate->addHtml($html); - } -} Index: includes/diff/Nodes.php =================================================================== --- includes/diff/Nodes.php (revision 53587) +++ includes/diff/Nodes.php (working copy) @@ -1,439 +0,0 @@ - - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * or see http://www.gnu.org/ - * - */ - -/** - * Any element in the DOM tree of an HTML document. - * @ingroup DifferenceEngine - */ -class Node { - - public $parent; - - protected $parentTree; - - public $whiteBefore = false; - - public $whiteAfter = false; - - function __construct($parent) { - $this->parent = $parent; - } - - public function getParentTree() { - if (!isset($this->parentTree)) { - if (!is_null($this->parent)) { - $this->parentTree = $this->parent->getParentTree(); - $this->parentTree[] = $this->parent; - } else { - $this->parentTree = array(); - } - } - return $this->parentTree; - } - - public function getLastCommonParent(Node $other) { - $result = new LastCommonParentResult(); - - $myParents = $this->getParentTree(); - $otherParents = $other->getParentTree(); - - $i = 1; - $isSame = true; - $nbMyParents = count($myParents); - $nbOtherParents = count($otherParents); - while ($isSame && $i < $nbMyParents && $i < $nbOtherParents) { - if (!$myParents[$i]->openingTag === $otherParents[$i]->openingTag) { - $isSame = false; - } else { - // After a while, the index i-1 must be the last common parent - $i++; - } - } - - $result->lastCommonParentDepth = $i - 1; - $result->parent = $myParents[$i - 1]; - - if (!$isSame || $nbMyParents > $nbOtherParents) { - // Not all tags matched, or all tags matched but - // there are tags left in this tree - $result->indexInLastCommonParent = $myParents[$i - 1]->getIndexOf($myParents[$i]); - $result->splittingNeeded = true; - } else if ($nbMyParents <= $nbOtherParents) { - $result->indexInLastCommonParent = $myParents[$i - 1]->getIndexOf($this); - } - return $result; - } - - public function setParent($parent) { - $this->parent = $parent; - unset($this->parentTree); - } - - public function inPre() { - $tree = $this->getParentTree(); - foreach ($tree as &$ancestor) { - if ($ancestor->isPre()) { - return true; - } - } - return false; - } -} - -/** - * Node that can contain other nodes. Represents an HTML tag. - * @ingroup DifferenceEngine - */ -class TagNode extends Node { - - public $children = array(); - - public $qName; - - public $attributes = array(); - - public $openingTag; - - function __construct($parent, $qName, /*array*/ $attributes) { - parent::__construct($parent); - $this->qName = strtolower($qName); - foreach($attributes as $key => &$value){ - $this->attributes[strtolower($key)] = $value; - } - return $this->openingTag = Xml::openElement($this->qName, $this->attributes); - } - - public function addChildAbsolute(Node $node, $index) { - array_splice($this->children, $index, 0, array($node)); - } - - public function getIndexOf(Node $child) { - // don't trust array_search with objects - foreach ($this->children as $key => &$value){ - if ($value === $child) { - return $key; - } - } - return null; - } - - public function getNbChildren() { - return count($this->children); - } - - public function getMinimalDeletedSet($id, &$allDeleted, &$somethingDeleted) { - $nodes = array(); - - $allDeleted = false; - $somethingDeleted = false; - $hasNonDeletedDescendant = false; - - if (empty($this->children)) { - return $nodes; - } - - foreach ($this->children as &$child) { - $allDeleted_local = false; - $somethingDeleted_local = false; - $childrenChildren = $child->getMinimalDeletedSet($id, $allDeleted_local, $somethingDeleted_local); - if ($somethingDeleted_local) { - $nodes = array_merge($nodes, $childrenChildren); - $somethingDeleted = true; - } - if (!$allDeleted_local) { - $hasNonDeletedDescendant = true; - } - } - if (!$hasNonDeletedDescendant) { - $nodes = array($this); - $allDeleted = true; - } - return $nodes; - } - - public function splitUntil(TagNode $parent, Node $split, $includeLeft) { - $splitOccured = false; - if ($parent !== $this) { - $part1 = new TagNode(null, $this->qName, $this->attributes); - $part2 = new TagNode(null, $this->qName, $this->attributes); - $part1->setParent($this->parent); - $part2->setParent($this->parent); - - $onSplit = false; - $pastSplit = false; - foreach ($this->children as &$child) - { - if ($child === $split) { - $onSplit = true; - } - if(!$pastSplit || ($onSplit && $includeLeft)) { - $child->setParent($part1); - $part1->children[] = $child; - } else { - $child->setParent($part2); - $part2->children[] = $child; - } - if ($onSplit) { - $onSplit = false; - $pastSplit = true; - } - } - $myindexinparent = $this->parent->getIndexOf($this); - if (!empty($part1->children)) { - $this->parent->addChildAbsolute($part1, $myindexinparent); - } - if (!empty($part2->children)) { - $this->parent->addChildAbsolute($part2, $myindexinparent); - } - if (!empty($part1->children) && !empty($part2->children)) { - $splitOccured = true; - } - - $this->parent->removeChild($myindexinparent); - - if ($includeLeft) { - $this->parent->splitUntil($parent, $part1, $includeLeft); - } else { - $this->parent->splitUntil($parent, $part2, $includeLeft); - } - } - return $splitOccured; - - } - - private function removeChild($index) { - unset($this->children[$index]); - $this->children = array_values($this->children); - } - - public static $blocks = array('html', 'body','p','blockquote', 'h1', - 'h2', 'h3', 'h4', 'h5', 'pre', 'div', 'ul', 'ol', 'li', 'table', - 'tbody', 'tr', 'td', 'th', 'br'); - - public function copyTree() { - $newThis = new TagNode(null, $this->qName, $this->attributes); - $newThis->whiteBefore = $this->whiteBefore; - $newThis->whiteAfter = $this->whiteAfter; - foreach ($this->children as &$child) { - $newChild = $child->copyTree(); - $newChild->setParent($newThis); - $newThis->children[] = $newChild; - } - return $newThis; - } - - public function getMatchRatio(TagNode $other) { - $txtComp = new TextOnlyComparator($other); - return $txtComp->getMatchRatio(new TextOnlyComparator($this)); - } - - public function expandWhiteSpace() { - $shift = 0; - $spaceAdded = false; - - $nbOriginalChildren = $this->getNbChildren(); - for ($i = 0; $i < $nbOriginalChildren; ++$i) { - $child = $this->children[$i + $shift]; - - if ($child instanceof TagNode) { - if (!$child->isPre()) { - $child->expandWhiteSpace(); - } - } - if (!$spaceAdded && $child->whiteBefore) { - $ws = new WhiteSpaceNode(null, ' ', $child->getLeftMostChild()); - $ws->setParent($this); - $this->addChildAbsolute($ws,$i + ($shift++)); - } - if ($child->whiteAfter) { - $ws = new WhiteSpaceNode(null, ' ', $child->getRightMostChild()); - $ws->setParent($this); - $this->addChildAbsolute($ws,$i + 1 + ($shift++)); - $spaceAdded = true; - } else { - $spaceAdded = false; - } - - } - } - - public function getLeftMostChild() { - if (empty($this->children)) { - return $this; - } - return $this->children[0]->getLeftMostChild(); - } - - public function getRightMostChild() { - if (empty($this->children)) { - return $this; - } - return $this->children[$this->getNbChildren() - 1]->getRightMostChild(); - } - - public function isPre() { - return 0 == strcasecmp($this->qName,'pre'); - } - - public static function toDiffLine(TagNode $node) { - return $node->openingTag; - } -} - -/** - * Represents a piece of text in the HTML file. - * @ingroup DifferenceEngine - */ -class TextNode extends Node { - - public $text; - - public $modification; - - function __construct($parent, $text) { - parent::__construct($parent); - $this->modification = new Modification(Modification::NONE); - $this->text = $text; - } - - public function copyTree() { - $clone = clone $this; - $clone->setParent(null); - return $clone; - } - - public function getLeftMostChild() { - return $this; - } - - public function getRightMostChild() { - return $this; - } - - public function getMinimalDeletedSet($id, &$allDeleted, &$somethingDeleted) { - if ($this->modification->type == Modification::REMOVED - && $this->modification->id == $id){ - $somethingDeleted = true; - $allDeleted = true; - return array($this); - } - return array(); - } - - public function isSameText($other) { - if (is_null($other) || ! $other instanceof TextNode) { - return false; - } - return str_replace('\n', ' ',$this->text) === str_replace('\n', ' ',$other->text); - } - - public static function toDiffLine(TextNode $node) { - return str_replace('\n', ' ',$node->text); - } -} - -/** - * @todo Document - * @ingroup DifferenceEngine - */ -class WhiteSpaceNode extends TextNode { - - function __construct($parent, $s, Node $like = null) { - parent::__construct($parent, $s); - if(!is_null($like) && $like instanceof TextNode) { - $newModification = clone $like->modification; - $newModification->firstOfID = false; - $this->modification = $newModification; - } - } -} - -/** - * Represents the root of a HTML document. - * @ingroup DifferenceEngine - */ -class BodyNode extends TagNode { - - function __construct() { - parent::__construct(null, 'body', array()); - } - - public function copyTree() { - $newThis = new BodyNode(); - foreach ($this->children as &$child) { - $newChild = $child->copyTree(); - $newChild->setParent($newThis); - $newThis->children[] = $newChild; - } - return $newThis; - } - - public function getMinimalDeletedSet($id, &$allDeleted, &$somethingDeleted) { - $nodes = array(); - foreach ($this->children as &$child) { - $childrenChildren = $child->getMinimalDeletedSet($id, - $allDeleted, $somethingDeleted); - $nodes = array_merge($nodes, $childrenChildren); - } - return $nodes; - } - -} - -/** - * Represents an image in HTML. Even though images do not contain any text they - * are independent visible objects on the page. They are logically a TextNode. - * @ingroup DifferenceEngine - */ -class ImageNode extends TextNode { - - public $attributes; - - function __construct(TagNode $parent, /*array*/ $attrs) { - if(!array_key_exists('src', $attrs)) { - HTMLDiffer::diffDebug( "Image without a source\n" ); - parent::__construct($parent, ''); - }else{ - parent::__construct($parent, '' . strtolower($attrs['src']) . ''); - } - $this->attributes = $attrs; - } - - public function isSameText($other) { - if (is_null($other) || ! $other instanceof ImageNode) { - return false; - } - return $this->text === $other->text; - } - -} - -/** - * No-op node - * @ingroup DifferenceEngine - */ -class DummyNode extends Node { - - function __construct() { - // no op - } - -} Index: includes/PageHistory.php =================================================================== --- includes/PageHistory.php (revision 53587) +++ includes/PageHistory.php (working copy) @@ -158,7 +158,7 @@ * @return string HTML output */ function beginHistoryList() { - global $wgUser, $wgScript, $wgEnableHtmlDiff; + global $wgUser, $wgScript; $this->lastdate = ''; $s = wfMsgExt( 'histlegend', array( 'parse') ); if( $this->linesonpage > 1 && $wgUser->isAllowed('deleterevision') ) { @@ -181,31 +181,13 @@ $s .= Xml::openElement( 'form', array( 'action' => $wgScript, 'id' => 'mw-history-compare' ) ); $s .= Xml::hidden( 'title', $this->mTitle->getPrefixedDbKey() ); - if( $wgEnableHtmlDiff ) { - $s .= $this->submitButton( wfMsg( 'visualcomparison'), - array( - 'name' => 'htmldiff', - 'class' => 'historysubmit', - 'accesskey' => wfMsg( 'accesskey-visualcomparison' ), - 'title' => wfMsg( 'tooltip-compareselectedversions' ), - ) - ); - $s .= $this->submitButton( wfMsg( 'wikicodecomparison'), - array( - 'class' => 'historysubmit', - 'accesskey' => wfMsg( 'accesskey-compareselectedversions' ), - 'title' => wfMsg( 'tooltip-compareselectedversions' ), - ) - ); - } else { - $s .= $this->submitButton( wfMsg( 'compareselectedversions'), - array( - 'class' => 'historysubmit', - 'accesskey' => wfMsg( 'accesskey-compareselectedversions' ), - 'title' => wfMsg( 'tooltip-compareselectedversions' ), - ) - ); - } + $s .= $this->submitButton( wfMsg( 'compareselectedversions'), + array( + 'class' => 'historysubmit', + 'accesskey' => wfMsg( 'accesskey-compareselectedversions' ), + 'title' => wfMsg( 'tooltip-compareselectedversions' ), + ) + ); $s .= ''; - if( $wgEnableHtmlDiff ) { - $s .= $this->submitButton( wfMsg( 'visualcomparison'), - array( - 'name' => 'htmldiff', - 'class' => 'historysubmit', - 'accesskey' => wfMsg( 'accesskey-visualcomparison' ), - 'title' => wfMsg( 'tooltip-compareselectedversions' ), - ) - ); - $s .= $this->submitButton( wfMsg( 'wikicodecomparison'), - array( - 'class' => 'historysubmit', - 'accesskey' => wfMsg( 'accesskey-compareselectedversions' ), - 'title' => wfMsg( 'tooltip-compareselectedversions' ), - ) - ); - } else { - $s .= $this->submitButton( wfMsg( 'compareselectedversions'), - array( - 'class' => 'historysubmit', - 'accesskey' => wfMsg( 'accesskey-compareselectedversions' ), - 'title' => wfMsg( 'tooltip-compareselectedversions' ), - ) - ); - } + $s .= $this->submitButton( wfMsg( 'compareselectedversions'), + array( + 'class' => 'historysubmit', + 'accesskey' => wfMsg( 'accesskey-compareselectedversions' ), + 'title' => wfMsg( 'tooltip-compareselectedversions' ), + ) + ); $s .= ''; return $s; } Index: languages/messages/MessagesEn.php =================================================================== --- languages/messages/MessagesEn.php (revision 53587) +++ languages/messages/MessagesEn.php (working copy) @@ -1525,64 +1525,8 @@ 'lineno' => 'Line $1:', 'compareselectedversions' => 'Compare selected revisions', 'showhideselectedversions' => 'Show/hide selected revisions', -'visualcomparison' => 'Visual comparison', -'wikicodecomparison' => 'Wikitext comparison', 'editundo' => 'undo', 'diff-multi' => '({{PLURAL:$1|One intermediate revision|$1 intermediate revisions}} not shown)', -'diff-movedto' => 'moved to $1', -'diff-styleadded' => '$1 style added', -'diff-added' => '$1 added', -'diff-changedto' => 'changed to $1', -'diff-movedoutof' => 'moved out of $1', -'diff-styleremoved' => '$1 style removed', -'diff-removed' => '$1 removed', -'diff-changedfrom' => 'changed from $1', -'diff-src' => 'source', -'diff-withdestination' => 'with destination $1', -'diff-with' => ' with $1 $2', -'diff-with-additional' => '$1 $2', # only translate this message to other languages if you have to change it -'diff-with-final' => ' and $1 $2', -'diff-width' => 'width', -'diff-height' => 'height', -'diff-p' => "a '''paragraph'''", -'diff-blockquote' => "a '''quote'''", -'diff-h1' => "a '''heading (level 1)'''", -'diff-h2' => "a '''heading (level 2)'''", -'diff-h3' => "a '''heading (level 3)'''", -'diff-h4' => "a '''heading (level 4)'''", -'diff-h5' => "a '''heading (level 5)'''", -'diff-pre' => "a '''preformatted block'''", -'diff-div' => "a '''division'''", -'diff-ul' => "an '''unordered list'''", -'diff-ol' => "an '''ordered list'''", -'diff-li' => "a '''list item'''", -'diff-table' => "a '''table'''", -'diff-tbody' => "a '''table's content'''", -'diff-tr' => "a '''row'''", -'diff-td' => "a '''cell'''", -'diff-th' => "a '''header'''", -'diff-br' => "a '''break'''", -'diff-hr' => "a '''horizontal rule'''", -'diff-code' => "a '''computer code block'''", -'diff-dl' => "a '''definition list'''", -'diff-dt' => "a '''definition term'''", -'diff-dd' => "a '''definition'''", -'diff-input' => "an '''input'''", -'diff-form' => "a '''form'''", -'diff-img' => "an '''image'''", -'diff-span' => "a '''span'''", -'diff-a' => "a '''link'''", -'diff-i' => "'''italics'''", -'diff-b' => "'''bold'''", -'diff-strong' => "'''strong'''", -'diff-em' => "'''emphasis'''", -'diff-font' => "'''font'''", -'diff-big' => "'''big'''", -'diff-del' => "'''deleted'''", -'diff-tt' => "'''fixed width'''", -'diff-sub' => "'''subscript'''", -'diff-sup' => "'''superscript'''", -'diff-strike' => "'''strikethrough'''", # Search results 'searchresults' => 'Search results', @@ -3174,7 +3118,6 @@ 'accesskey-preview' => 'p', # do not translate or duplicate this message to other languages 'accesskey-diff' => 'v', # do not translate or duplicate this message to other languages 'accesskey-compareselectedversions' => 'v', # do not translate or duplicate this message to other languages -'accesskey-visualcomparison' => 'b', # do not translate or duplicate this message to other languages 'accesskey-watch' => 'w', # do not translate or duplicate this message to other languages 'accesskey-upload' => 's', # do not translate or duplicate this message to other languages @@ -3369,9 +3312,6 @@ 'previousdiff' => '← Older edit', 'nextdiff' => 'Newer edit →', -# Visual comparison -'visual-comparison' => 'Visual comparison', - # Media information 'mediawarning' => "'''Warning''': This file may contain malicious code, by executing it your system may be compromised.
", 'imagemaxsize' => "Image size limit:
''(for file description pages)''", Index: maintenance/language/messages.inc =================================================================== --- maintenance/language/messages.inc (revision 53587) +++ maintenance/language/messages.inc (working copy) @@ -739,64 +739,7 @@ 'lineno', 'compareselectedversions', 'showhideselectedversions', - 'visualcomparison', - 'wikicodecomparison', 'editundo', - 'diff-multi', - 'diff-movedto', - 'diff-styleadded', - 'diff-added', - 'diff-changedto', - 'diff-movedoutof', - 'diff-styleremoved', - 'diff-removed', - 'diff-changedfrom', - 'diff-src', - 'diff-withdestination', - 'diff-with', - 'diff-with-additional', - 'diff-with-final', - 'diff-width', - 'diff-height', - 'diff-p', - 'diff-blockquote', - 'diff-h1', - 'diff-h2', - 'diff-h3', - 'diff-h4', - 'diff-h5', - 'diff-pre', - 'diff-div', - 'diff-ul', - 'diff-ol', - 'diff-li', - 'diff-table', - 'diff-tbody', - 'diff-tr', - 'diff-td', - 'diff-th', - 'diff-br', - 'diff-hr', - 'diff-code', - 'diff-dl', - 'diff-dt', - 'diff-dd', - 'diff-input', - 'diff-form', - 'diff-img', - 'diff-span', - 'diff-a', - 'diff-i', - 'diff-b', - 'diff-strong', - 'diff-em', - 'diff-font', - 'diff-big', - 'diff-del', - 'diff-tt', - 'diff-sub', - 'diff-sup', - 'diff-strike', ), 'search' => array( 'searchresults', @@ -2186,7 +2129,6 @@ 'accesskey-preview', 'accesskey-diff', 'accesskey-compareselectedversions', - 'accesskey-visualcomparison', 'accesskey-watch', 'accesskey-upload', ), @@ -2374,9 +2316,6 @@ 'previousdiff', 'nextdiff', ), - 'visual-comparison' => array( - 'visual-comparison', - ), 'media-info' => array( 'mediawarning', 'imagemaxsize', @@ -3206,7 +3145,6 @@ 'variantname-kk' => 'Variants for Kazakh language', 'variantname-ku' => 'Variants for Kurdish language', 'variantname-tg' => 'Variants for Tajiki language', - 'visual-comparison' => 'Visual comparison', 'media-info' => 'Media information', 'metadata' => 'Metadata', 'exif' => 'EXIF tags', Index: maintenance/language/messageTypes.inc =================================================================== --- maintenance/language/messageTypes.inc (revision 53587) +++ maintenance/language/messageTypes.inc (working copy) @@ -65,7 +65,6 @@ 'accesskey-preview', 'accesskey-diff', 'accesskey-compareselectedversions', - 'accesskey-visualcomparison', 'accesskey-watch', 'accesskey-upload', 'addsection',