diff -r 902822492a68 -r fe660c52c48f includes/diffengine/Renderer/xhtml.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/diffengine/Renderer/xhtml.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,262 @@ +\n+"; + + /** + * Suffix for inserted text. + */ + var $_ins_suffix = "\n\n\n"; + + /** + * Prefix for deleted text. + */ + var $_del_prefix = "\n-"; + + /** + * Suffix for deleted text. + */ + var $_del_suffix = "\n\n\n"; + + /** + * Header for each change block. + */ + var $_block_header = ''; + + /** + * What are we currently splitting on? Used to recurse to show word-level + * changes. + */ + var $_split_level = 'lines'; + + function _blockHeader($xbeg, $xlen, $ybeg, $ylen) + { + return "\nLine $xbeg: {$this->_block_header}"; + } + + function _startBlock($header) + { + return $header; + } + + function _lines($lines, $prefix = ' ', $encode = true) + { + if ($encode) { + array_walk($lines, array(&$this, '_encode')); + } + + if ($this->_split_level == 'words') { + return implode('', $lines); + } else { + return implode("
", $lines) . "\n"; + } + } + + function _added($lines) + { + array_walk($lines, array(&$this, '_encode')); + $lines[0] = $this->_ins_prefix . $lines[0]; + $lines[count($lines) - 1] .= $this->_ins_suffix; + return $this->_lines($lines, ' ', false); + } + + function _deleted($lines, $words = false) + { + array_walk($lines, array(&$this, '_encode')); + $lines[0] = $this->_del_prefix . $lines[0]; + $lines[count($lines) - 1] .= $this->_del_suffix; + return $this->_lines($lines, ' ', false); + } + + function _context($lines) + { + return "\n".$this->_lines($lines).''."\n\n\n"; + } + + function _changed($orig, $final) + { + /* If we've already split on words, don't try to do so again - just display. */ + if ($this->_split_level == 'words') { + $prefix = ''; + while ($orig[0] !== false && $final[0] !== false && + substr($orig[0], 0, 1) == ' ' && + substr($final[0], 0, 1) == ' ') { + $prefix .= substr($orig[0], 0, 1); + $orig[0] = substr($orig[0], 1); + $final[0] = substr($final[0], 1); + } + $ret = $prefix . $this->_deleted($orig) . $this->_added($final) . "\n"; + //echo 'DEBUG:
'.htmlspecialchars($ret).'
'; + return $ret; + } + + $text1 = implode("\n", $orig); + $text2 = implode("\n", $final); + + /* Non-printing newline marker. */ + $nl = "\0"; + + /* We want to split on word boundaries, but we need to + * preserve whitespace as well. Therefore we split on words, + * but include all blocks of whitespace in the wordlist. */ + $diff = &new Text_Diff($this->_splitOnWords($text1, $nl), + $this->_splitOnWords($text2, $nl)); + + /* Get the diff in inline format. */ + $renderer = &new Text_Diff_Renderer_inline(array_merge($this->getParams(), + array('split_level' => 'words'))); + + /* Run the diff and get the output. */ + $ret = str_replace($nl, "
", $renderer->render($diff)); + //echo 'DEBUG:
'.htmlspecialchars($ret).'
'; + return $ret . "\n"; + } + + function _splitOnWords($string, $newlineEscape = "
") + { + $words = array(); + $length = strlen($string); + $pos = 0; + + while ($pos < $length) { + // Eat a word with any preceding whitespace. + $spaces = strspn(substr($string, $pos), " \n"); + $nextpos = strcspn(substr($string, $pos + $spaces), " \n"); + $words[] = str_replace("\n", $newlineEscape, substr($string, $pos, $spaces + $nextpos)); + $pos += $spaces + $nextpos; + } + + return $words; + } + + function _encode(&$string) + { + $string = htmlspecialchars($string); + } + + /** + * Renders a diff. + * + * @param Text_Diff $diff A Text_Diff object. + * + * @return string The formatted output. + */ + + function render($diff) + { + $xi = $yi = 1; + $block = false; + $context = array(); + + $nlead = $this->_leading_context_lines; + $ntrail = $this->_trailing_context_lines; + + $output = $this->_startDiff(); + + $diffs = $diff->getDiff(); + foreach ($diffs as $i => $edit) { + if (is_a($edit, 'Text_Diff_Op_copy')) { + if (is_array($block)) { + $keep = $i == count($diffs) - 1 ? $ntrail : $nlead + $ntrail; + if (count($edit->orig) <= $keep) { + $block[] = $edit; + } else { + if ($ntrail) { + $context = array_slice($edit->orig, 0, $ntrail); + $block[] = &new Text_Diff_Op_copy($context); + } + $bk = $this->_block($x0, $ntrail + $xi - $x0, + $y0, $ntrail + $yi - $y0, + $block); + $output .= $bk; + $block = false; + } + } + $context = $edit->orig; + } else { + if (!is_array($block)) { + $context = array_slice($context, count($context) - $nlead); + $x0 = $xi - count($context); + $y0 = $yi - count($context); + $block = array(); + if ($context) { + $block[] = &new Text_Diff_Op_copy($context); + } + } + $block[] = $edit; + } + + if ($edit->orig) { + $xi += count($edit->orig); + } + if ($edit->final) { + $yi += count($edit->final); + } + } + + if (is_array($block)) { + $bk = $this->_block($x0, $xi - $x0, + $y0, $yi - $y0, + $block); + $output .= $bk; + } + + $final = $output . $this->_endDiff(); + if ($final == '') $final = 'No differences.'; + //$final = preg_replace('#('.preg_quote($this->_ins_suffix).'|'.preg_quote($this->_del_suffix).')(.+?)('.preg_quote($this->_ins_prefix).'|'.preg_quote($this->_ins_suffix).')#', '\\1'.$final.''."\n\n"; + } + + function _block($xbeg, $xlen, $ybeg, $ylen, &$edits) + { + $output = $this->_startBlock($this->_blockHeader($xbeg, $xlen, $ybeg, $ylen)); + + foreach ($edits as $edit) { + switch (strtolower(get_class($edit))) { + case 'text_diff_op_copy': + $output .= $this->_context($edit->orig); + break; + + case 'text_diff_op_add': + $output .= $this->_added($edit->final); + break; + + case 'text_diff_op_delete': + $output .= $this->_deleted($edit->orig); + break; + + case 'text_diff_op_change': + $output .= $this->_changed($edit->orig, $edit->final); + break; + } + } + + return $output . $this->_endBlock(); + } + +}