Encapsulate HTML diff generation in a view helper
authorChuck Hagenbuch <chuck@horde.org>
Sat, 13 Nov 2010 18:57:57 +0000 (13:57 -0500)
committerChuck Hagenbuch <chuck@horde.org>
Sun, 14 Nov 2010 04:37:19 +0000 (23:37 -0500)
18 files changed:
chora/app/helpers/Diff.php [new file with mode: 0644]
chora/app/views/diff/added.html.php [new file with mode: 0644]
chora/app/views/diff/caption.html.php [new file with mode: 0644]
chora/app/views/diff/change.html.php [new file with mode: 0644]
chora/app/views/diff/context.html.php [new file with mode: 0644]
chora/app/views/diff/diff.html.php [new file with mode: 0644]
chora/app/views/diff/removed.html.php [new file with mode: 0644]
chora/diff.php
chora/templates/diff/header.inc [new file with mode: 0644]
chora/templates/diff/hr/add.inc [deleted file]
chora/templates/diff/hr/change.inc [deleted file]
chora/templates/diff/hr/empty.inc [deleted file]
chora/templates/diff/hr/footer.inc [deleted file]
chora/templates/diff/hr/header.inc [deleted file]
chora/templates/diff/hr/nochange.inc [deleted file]
chora/templates/diff/hr/remove.inc [deleted file]
chora/templates/diff/hr/row.inc [deleted file]
chora/themes/screen.css

diff --git a/chora/app/helpers/Diff.php b/chora/app/helpers/Diff.php
new file mode 100644 (file)
index 0000000..87b1e11
--- /dev/null
@@ -0,0 +1,86 @@
+<?php
+/**
+ * Copyright 2000-2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author  Chuck Hagenbuch <chuck@horde.org>
+ * @package Chora
+ */
+class Chora_Diff_Helper extends Horde_View_Helper_Base
+{
+    /**
+     * @var string
+     */
+    protected $_context = '';
+
+    public function diff($diff)
+    {
+        if (!$diff) {
+            return '<p>' . _("No Visible Changes") . '</p>';
+        }
+
+        return $this->render('app/views/diff/diff.html.php', array('diff' => $diff));
+    }
+
+    public function diffAdd($change)
+    {
+        return $this->render('app/views/diff/added.html.php', array(
+            'lines' => $change['lines'],
+        ));
+    }
+
+    public function diffRemove($change)
+    {
+        return $this->render('app/views/diff/removed.html.php', array(
+            'lines' => $change['lines'],
+        ));
+    }
+
+    public function diffEmpty($change)
+    {
+        $this->_context .= $this->escape($change['line']) . '<br>';
+        return '';
+    }
+
+    public function diffChange($change)
+    {
+        // Pop the old/new stacks one by one, until both are empty.
+        $oldsize = count($change['old']);
+        $newsize = count($change['new']);
+        $left = $right = '';
+        for ($row = 0, $rowMax = max($oldsize, $newsize); $row < $rowMax; ++$row) {
+            $left .= (isset($change['old'][$row]) ? $this->escape($change['old'][$row]) : '') . '<br>';
+            $right .= (isset($change['new'][$row]) ? $this->escape($change['new'][$row]) : '') . '<br>';
+        }
+
+        return $this->render('app/views/diff/change.html.php', array(
+            'left' => $left,
+            'right' => $right,
+            'oldsize' => $oldsize,
+            'newsize' => $newsize,
+            'row' => $row,
+        ));
+    }
+
+    public function hasContext()
+    {
+        return !empty($this->_context);
+    }
+
+    public function diffContext()
+    {
+        $context = $this->_context;
+        $this->_context = '';
+
+        return $this->render('app/views/diff/context.html.php', array(
+            'context' => $context,
+        ));
+    }
+
+    public function diffCaption()
+    {
+        return $this->render('app/views/diff/caption.html.php');
+    }
+}
diff --git a/chora/app/views/diff/added.html.php b/chora/app/views/diff/added.html.php
new file mode 100644 (file)
index 0000000..d889322
--- /dev/null
@@ -0,0 +1,4 @@
+<tr>
+ <td class="diff-added-empty"></td>
+ <td class="diff-added"><div class="diff"><pre><?php foreach ($lines as $l) echo $this->escape($l) . '<br>' ?></pre></div></td>
+</tr>
diff --git a/chora/app/views/diff/caption.html.php b/chora/app/views/diff/caption.html.php
new file mode 100644 (file)
index 0000000..706eb44
--- /dev/null
@@ -0,0 +1,7 @@
+<div class="diff-caption">
+<?php $blank = Horde_Themes::img('blank.gif') ?>
+<img class="diff-unmodified" src="<?php echo $blank ?>" alt="<?php echo _("Unmodified") ?>" /> <?php echo _("Unmodified") ?>
+<img class="diff-added" src="<?php echo $blank ?>" alt="<?php echo _("Added") ?>" /> <?php echo _("Added") ?>
+<img class="diff-modified" src="<?php echo $blank ?>" alt="<?php echo _("Modified") ?>" /> <?php echo _("Modified") ?>
+<img class="diff-removed" src="<?php echo $blank ?>" alt="<?php echo _("Removed") ?>" /> <?php echo _("Removed") ?>
+</div>
diff --git a/chora/app/views/diff/change.html.php b/chora/app/views/diff/change.html.php
new file mode 100644 (file)
index 0000000..d54ba5d
--- /dev/null
@@ -0,0 +1,20 @@
+<tr>
+<?php if (!empty($left)): ?>
+ <td class="diff-modified">
+  <div class="diff"><pre><?php echo $left ?></pre></div>
+ </td>
+<?php elseif ($row < $oldsize): ?>
+ <td class="diff-modified"></td>
+<?php else: ?>
+ <td class="diff-unmodified"></td>
+<?php endif; ?>
+<?php if (!empty($right)): ?>
+ <td class="diff-modified">
+  <div class="diff"><pre><?php echo $right ?></pre></div>
+ </td>
+<?php elseif ($row < $newsize): ?>
+ <td class="diff-modified"></td>
+<?php else: ?>
+ <td class="diff-unmodified"></td>
+<?php endif; ?>
+</tr>
diff --git a/chora/app/views/diff/context.html.php b/chora/app/views/diff/context.html.php
new file mode 100644 (file)
index 0000000..4658932
--- /dev/null
@@ -0,0 +1,4 @@
+<tr class="diff-unmodified">
+ <td><div class="diff"><pre><?php echo $context ?></pre></div></td>
+ <td><div class="diff"><pre><?php echo $context ?></pre></div></td>
+</tr>
diff --git a/chora/app/views/diff/diff.html.php b/chora/app/views/diff/diff.html.php
new file mode 100644 (file)
index 0000000..64c0425
--- /dev/null
@@ -0,0 +1,24 @@
+<table cellspacing="0" class="diff">
+<?php foreach ($diff as $section): ?>
+<tbody>
+<tr>
+ <th><?php printf(_("Line %s"), $section['oldline']) ?></th>
+ <th><?php printf(_("Line %s"), $section['newline']) ?></th>
+</tr>
+<?php
+foreach ($section['contents'] as $change) {
+    if ($this->hasContext() && $change['type'] != 'empty') {
+        echo $this->diffContext();
+    }
+
+    $method = 'diff' . ucfirst($change['type']);
+    echo $this->$method($change);
+}
+
+if ($this->hasContext()) {
+    echo $this->diffContext();
+}
+?>
+</tbody>
+<?php endforeach ?>
+</table>
diff --git a/chora/app/views/diff/removed.html.php b/chora/app/views/diff/removed.html.php
new file mode 100644 (file)
index 0000000..ca3d2a6
--- /dev/null
@@ -0,0 +1,4 @@
+<tr>
+ <td class="diff-removed"><div class="diff"><pre><?php foreach ($lines as $l) echo $this->escape($l) . '<br>' ?></pre></div></td>
+ <td class="diff-removed-empty"></td>
+</tr>
index c484cfb..7d890b2 100644 (file)
@@ -78,14 +78,11 @@ foreach ($VC->getRevisionRange($fl, $r1, $r2) as $val) {
     }
 }
 
-/* Get list of diff types. */
-$diff_types = array_flip($VC->availableDiffTypes());
-
 Horde::addScriptFile('stripe.js', 'horde');
 require CHORA_TEMPLATES . '/common-header.inc';
 require CHORA_TEMPLATES . '/menu.inc';
 require CHORA_TEMPLATES . '/headerbar.inc';
-require CHORA_TEMPLATES . '/diff/hr/header.inc';
+require CHORA_TEMPLATES . '/diff/header.inc';
 
 $mime_type = Horde_Mime_Magic::filenameToMIME($fullname);
 if (substr($mime_type, 0, 6) == 'image/') {
@@ -96,74 +93,10 @@ if (substr($mime_type, 0, 6) == 'image/') {
     echo "<tr><td><img src=\"$url1\" alt=\"" . htmlspecialchars($r1) . '" /></td>' .
         "<td><img src=\"$url2\" alt=\"" . htmlspecialchars($r2) . '" /></td></tr>';
 } else {
-    /* Retrieve the tree of changes. */
-    $lns = $VC->diff($fl, $r1, $r2, array('human' => true, 'num' => $num, 'ws' => $ws));
-    if (!$lns) {
-        /* Is the diff empty? */
-        require CHORA_TEMPLATES . '/diff/hr/nochange.inc';
-    } else {
-        /* Iterate through every header block of changes. */
-        foreach ($lns as $header) {
-            $lefthead = $header['oldline'];
-            $righthead = $header['newline'];
-            require CHORA_TEMPLATES . '/diff/hr/row.inc';
-
-            /* Each header block consists of a number of changes
-             * (add, remove, change). */
-            $curContext = '';
-            foreach ($header['contents'] as $change) {
-                if (!empty($curContext) && $change['type'] != 'empty') {
-                    $line = $curContext;
-                    $curContext = '';
-                    require CHORA_TEMPLATES . '/diff/hr/empty.inc';
-                }
-
-                switch ($change['type']) {
-                case 'add':
-                    $line = '';
-                    foreach ($change['lines'] as $l) {
-                        $line .= htmlspecialchars($l) . '<br />';
-                    }
-                    require CHORA_TEMPLATES . '/diff/hr/add.inc';
-                    break;
-
-                case 'remove':
-                    $line = '';
-                    foreach ($change['lines'] as $l) {
-                        $line .= htmlspecialchars($l) . '<br />';
-                    }
-                    require CHORA_TEMPLATES . '/diff/hr/remove.inc';
-                    break;
-
-                case 'empty':
-                    $curContext .= htmlspecialchars($change['line']) . '<br />';
-                    break;
-
-                case 'change':
-                    /* Pop the old/new stacks one by one, until both are
-                     * empty. */
-                    $oldsize = count($change['old']);
-                    $newsize = count($change['new']);
-                    $left = $right = '';
-                    for ($row = 0, $rowMax = max($oldsize, $newsize); $row < $rowMax; ++$row) {
-                        $left .= isset($change['old'][$row]) ? htmlspecialchars($change['old'][$row]) : '';
-                        $left .= '<br />';
-                        $right .= isset($change['new'][$row]) ? htmlspecialchars($change['new'][$row]) : '';
-                        $right .= '<br />';
-                    }
-                    require CHORA_TEMPLATES . '/diff/hr/change.inc';
-                    break;
-                }
-            }
-
-            if (!empty($curContext)) {
-                $line = $curContext;
-                $curContext = '';
-                require CHORA_TEMPLATES . '/diff/hr/empty.inc';
-            }
-        }
-    }
+    $view = $injector->createInstance('Horde_View_Base');
+    $view->addHelper('Chora_Diff_Helper');
+    echo $view->diff($VC->diff($fl, $r1, $r2, array('human' => true, 'num' => $num, 'ws' => $ws)));
+    echo $view->diffCaption();
 }
 
-require CHORA_TEMPLATES . '/diff/hr/footer.inc';
 require $registry->get('templates', 'horde') . '/common-footer.inc';
diff --git a/chora/templates/diff/header.inc b/chora/templates/diff/header.inc
new file mode 100644 (file)
index 0000000..2ebe29d
--- /dev/null
@@ -0,0 +1,40 @@
+<div class="options">
+ <div>
+  <form method="get" action="diff.php">
+   <?php echo Chora::formInputs() ?>
+   <input type="hidden" name="f" value="<?php echo htmlspecialchars(Horde_Util::getFormData('f')) ?>" />
+   <input type="hidden" name="r1" value="<?php echo htmlspecialchars(Horde_Util::getFormData('r1')) ?>" />
+   <input type="hidden" name="r2" value="<?php echo htmlspecialchars(Horde_Util::getFormData('r2')) ?>" />
+   <?php echo _("Download diff as: ") ?>
+   <select name="t" onchange="this.form.submit()">
+    <option value="unified"><?php echo _("Unified") ?></option>
+   </select>
+   <input class="button" type="submit" value="<?php echo _("Get Diff") ?>" /><br />
+  </form>
+ </div>
+</div>
+
+<?php if (!empty($log_messages)): ?>
+<h3 class="revision_log"><?php echo _("Log Message") ?></h3>
+<div class="revision_log">
+<?php foreach ($log_messages as $val): ?>
+ <div class="difflog">
+  <ul class="revision striped">
+   <?php if (!empty($val['author'])): ?><li><?php echo _("Author:") ?> <?php echo $val['author'] ?></li><?php endif; ?>
+   <?php if (!empty($val['date'])): ?><li><?php echo _("Date:") ?> <?php echo $val['date'] ?></li><?php endif; ?>
+<?php if (!empty($val['branchinfo'])): ?>
+   <li><?php echo _("Branch:") ?>
+<?php foreach ($val['branchinfo'] as $branchname): ?>
+    <strong><a href="<?php echo Chora::url('browsefile', $where, array('onb' => $branchname)) ?>"><?php echo $branchname ?></a></strong>
+<?php endforeach; ?>
+   </li>
+<?php endif; ?>
+   <?php if (!empty($val['tags'])): ?><li class="tags"><?php echo _("Tags:") ?> <?php echo implode(', ', $val['tags']) ?></li><?php endif; ?>
+   <?php if (isset($val['added'])): ?><li><?php echo _("Changes:") ?> <span class="diffadd">+<?php echo htmlspecialchars($val['added']) ?></span>, <span class="diffdel">-<?php echo htmlspecialchars($val['deleted']) ?></span> <?php echo _("lines") ?></li><?php endif; ?>
+  </ul>
+  <a href="<?php echo Chora::url('co', $where, array('r' => $val['rev'])) ?>" title="<?php echo htmlspecialchars($val['rev']) ?>"><?php echo htmlspecialchars($VC->abbrev($val['rev'])) ?></a>: <?php echo $val['msg'] ?>
+  <div class="diffclear"></div>
+ </div>
+<?php endforeach; ?>
+</div>
+<?php endif; ?>
diff --git a/chora/templates/diff/hr/add.inc b/chora/templates/diff/hr/add.inc
deleted file mode 100644 (file)
index 2a0908d..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-<tr>
- <td class="added_empty">&nbsp;</td>
- <td class="added"><pre><?php echo strlen($line) ? $line : '&nbsp;' ?></pre></td>
-</tr>
diff --git a/chora/templates/diff/hr/change.inc b/chora/templates/diff/hr/change.inc
deleted file mode 100644 (file)
index abbd37a..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-<tr>
-<?php if (!empty($left)): ?>
- <td class="modified">
-  <pre><?php echo $left ?></pre>
- </td>
-<?php elseif ($row < $oldsize): ?>
- <td class="modified">&nbsp;</td>
-<?php else: ?>
- <td class="unmodified">&nbsp;</td>
-<?php endif; ?>
-<?php if (!empty($right)): ?>
- <td class="modified">
-  <pre><?php echo $right ?></pre>
- </td>
-<?php elseif ($row < $newsize): ?>
- <td class="modified">&nbsp;</td>
-<?php else: ?>
- <td class="unmodified">&nbsp;</td>
-<?php endif; ?>
-</tr>
diff --git a/chora/templates/diff/hr/empty.inc b/chora/templates/diff/hr/empty.inc
deleted file mode 100644 (file)
index 99f1b9f..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-<tr class="unmodified">
- <td><pre><?php echo strlen($line) ? $line : '&nbsp;' ?></pre></td>
- <td><pre><?php echo strlen($line) ? $line : '&nbsp;' ?></pre></td>
-</tr>
-
diff --git a/chora/templates/diff/hr/footer.inc b/chora/templates/diff/hr/footer.inc
deleted file mode 100644 (file)
index 02ee59f..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-</tbody>
-</table>
diff --git a/chora/templates/diff/hr/header.inc b/chora/templates/diff/hr/header.inc
deleted file mode 100644 (file)
index 4f9b92f..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-<div class="options">
- <div>
-  <form method="get" action="diff.php">
-   <?php echo Chora::formInputs() ?>
-   <input type="hidden" name="f" value="<?php echo htmlspecialchars(Horde_Util::getFormData('f')) ?>" />
-   <input type="hidden" name="r1" value="<?php echo htmlspecialchars(Horde_Util::getFormData('r1')) ?>" />
-   <input type="hidden" name="r2" value="<?php echo htmlspecialchars(Horde_Util::getFormData('r2')) ?>" />
-   <?php echo _("Download diff as: ") ?>
-   <select name="t" onchange="this.form.submit()">
-<?php if (isset($diff_types['unified'])): ?>
-    <option value="unified"><?php echo _("Unified") ?></option>
-<?php endif; ?>
-<?php if (isset($diff_types['context'])): ?>
-    <option value="context"><?php echo _("Context") ?></option>
-<?php endif; ?>
-<?php if (isset($diff_types['column'])): ?>
-    <option value="column"><?php echo _("Column") ?></option>
-<?php endif; ?>
-<?php if (isset($diff_types['ed'])): ?>
-    <option value="ed"><?php echo _("Ed Script") ?></option>
-<?php endif; ?>
-   </select>
-   <input class="button" type="submit" value="<?php echo _("Get Diff") ?>" /><br />
-  </form>
- </div>
-</div>
-
-<?php if (!empty($log_messages)): ?>
-<h3 class="revision_log"><?php echo _("Log Message") ?></h3>
-<div class="revision_log">
-<?php foreach ($log_messages as $val): ?>
- <div class="difflog">
-  <ul class="revision striped">
-   <?php if (!empty($val['author'])): ?><li><?php echo _("Author:") ?> <?php echo $val['author'] ?></li><?php endif; ?>
-   <?php if (!empty($val['date'])): ?><li><?php echo _("Date:") ?> <?php echo $val['date'] ?></li><?php endif; ?>
-<?php if (!empty($val['branchinfo'])): ?>
-   <li><?php echo _("Branch:") ?>
-<?php foreach ($val['branchinfo'] as $branchname): ?>
-    <strong><a href="<?php echo Chora::url('browsefile', $where, array('onb' => $branchname)) ?>"><?php echo $branchname ?></a></strong>
-<?php endforeach; ?>
-   </li>
-<?php endif; ?>
-   <?php if (!empty($val['tags'])): ?><li class="tags"><?php echo _("Tags:") ?> <?php echo implode(', ', $val['tags']) ?></li><?php endif; ?>
-   <?php if (isset($val['added'])): ?><li><?php echo _("Changes:") ?> <span class="diffadd">+<?php echo htmlspecialchars($val['added']) ?></span>, <span class="diffdel">-<?php echo htmlspecialchars($val['deleted']) ?></span> <?php echo _("lines") ?></li><?php endif; ?>
-  </ul>
-  <a href="<?php echo Chora::url('co', $where, array('r' => $val['rev'])) ?>" title="<?php echo htmlspecialchars($val['rev']) ?>"><?php echo htmlspecialchars($VC->abbrev($val['rev'])) ?></a>: <?php echo $val['msg'] ?>
-  <div class="diffclear"></div>
- </div>
-<?php endforeach; ?>
-</div>
-<?php endif; ?>
-
-<table cellspacing="0" class="hrdiff">
-<caption>
- <?php $blank = Horde_Themes::img('blank.gif') ?>
- <img class="unmodified" src="<?php echo $blank ?>" alt="<?php echo _("Unmodified") ?>" /> <?php echo _("Unmodified") ?>
- <img class="added" src="<?php echo $blank ?>" alt="<?php echo _("Added") ?>" /> <?php echo _("Added") ?>
- <img class="modified" src="<?php echo $blank ?>" alt="<?php echo _("Modified") ?>" /> <?php echo _("Modified") ?>
- <img class="removed" src="<?php echo $blank ?>" alt="<?php echo _("Removed") ?>" /> <?php echo _("Removed") ?>
-</caption>
-<thead>
-<tr>
- <th><a href="<?php echo Chora::url('co', $where, array('r' => $r1)) ?>" title="<?php echo htmlspecialchars($r1) ?>"><?php printf(_("Version %s"), htmlspecialchars($abbrev_r1)) ?></a></th>
- <th><a href="<?php echo Chora::url('co', $where, array('r' => $r2)) ?>" title="<?php echo htmlspecialchars($r2) ?>"><?php printf(_("Version %s"), htmlspecialchars($abbrev_r2)) ?></a></th>
-</tr>
-</thead>
-<tbody>
diff --git a/chora/templates/diff/hr/nochange.inc b/chora/templates/diff/hr/nochange.inc
deleted file mode 100644 (file)
index 05b8186..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-<tr>
- <th>
-  <?php echo _("No Visible Changes") ?>
- </th>
-</tr>
diff --git a/chora/templates/diff/hr/remove.inc b/chora/templates/diff/hr/remove.inc
deleted file mode 100644 (file)
index e6c020a..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-<tr>
- <td class="removed"><pre><?php echo strlen($line) ? $line : '&nbsp;' ?></pre></td>
- <td class="removed_empty">&nbsp;</td>
-</tr>
diff --git a/chora/templates/diff/hr/row.inc b/chora/templates/diff/hr/row.inc
deleted file mode 100644 (file)
index c115de3..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-<tr>
- <th>
-  <?php printf(_("Line %s"), $lefthead) ?>
- </th>
- <th>
-  <?php printf(_("Line %s"), $righthead) ?>
- </th>
-</tr>
index 5b287ec..bb7408c 100644 (file)
@@ -12,7 +12,7 @@ form#repository-picker {
 
 /* Options boxes. */
 div.options, div.browsefileoptions {
-    margin: 0 0 8px 5px;
+    margin: 0 0 8px 0;
 }
 div.options div, div.browsefileoptions div {
     display: -moz-inline-stack;
@@ -34,9 +34,9 @@ div.browsefileoptions form {
 
 /* Tables. */
 table.revlog, table.headerbar, table.browse, table.stats {
-    width: 99%;
+    width: 100%;
     font-size: 100%;
-    margin: 0 0 8px 5px;
+    margin: 0 0 8px 0;
     border-top: 1px solid #ddd;
     border-left: 1px solid #ddd;
 }
@@ -66,7 +66,7 @@ table.headerbar td {
 
 /* History View. */
 table.history, p.history {
-    margin: 0 0 8px 5px;
+    margin: 0 0 8px 0;
     border: 1px solid #ddd;
 }
 table.history a {
@@ -76,7 +76,7 @@ table.history a {
 
 /* Individual revision information. */
 div.revision_log {
-    margin: 2px 5px 8px 5px;
+    margin: 2px 5px 8px 0;
     border: 1px solid #ddd;
     padding: 3px;
 }
@@ -95,7 +95,7 @@ h3.revision_log, h3.checkout, h3.file-view-header {
     background: #e9e9e9;
     display: inline;
     font-weight: bold;
-    margin: 0 0 0 5px;
+    margin: 0;
     font-size: 90%;
     padding: 2px;
     border: 1px solid #ddd;
@@ -185,7 +185,7 @@ ul.singlepsfiles li {
 /* Checkout, File view */
 div.checkout, div.file-view-contents {
     padding: 3px;
-    margin: 2px 5px 8px 5px;
+    margin: 2px 5px 8px 0;
     border: 1px solid #ddd;
 }
 div.file-view {
@@ -200,7 +200,7 @@ div.file-view {
 /* Annotate styles. */
 table.annotate {
     width: 99%;
-    margin: 0 0 8px 5px;
+    margin: 0 0 8px 0;
     border-top: 1px solid #ddd;
     border-left: 1px solid #ddd;
     border-bottom: 1px solid #ddd;
@@ -224,54 +224,49 @@ table.annotate .logentry {
 }
 
 /* Diff styles. */
-table.hrdiff {
-    width: 99%;
+table.diff {
+    width: 100%;
     font-size: 100%;
-    margin: 0 0 8px 5px;
-}
-table.hrdiff th {
-    text-align: left;
-    width: 50%;
+    margin: 0 0 8px 0;
 }
-table.hrdiff thead th {
+table.diff th {
+    font-size: 80%;
     font-weight: bold;
-    font-size: 110%;
-    padding: 2px;
 }
-table.hrdiff tbody th {
-    padding: 2em 1px 1px 1px;
-    font-size: 80%;
+
+div.diff {
+    overflow: auto;
 }
-table.hrdiff .unmodified {
+
+.diff-unmodified {
     background: #fff;
+    color: #666;
 }
-table.hrdiff .added {
-    background: #9f9;
+.diff-added {
+    background: #afa;
 }
-table.hrdiff .added_empty {
+.diff-added-empty {
     background: #cfc;
 }
-table.hrdiff .modified {
-    background: #fd9;
+.diff-modified {
+    background: #cdf;
 }
-table.hrdiff .removed {
+.diff-removed {
     background: #f99;
 }
-table.hrdiff .removed_empty {
+.diff-removed-empty {
     background: #fcc;
 }
-table.hrdiff caption {
-    caption-side: top;
+
+.diff-caption {
     text-align: left;
-    margin: 0 0 8px 5px;
+    margin: 0 0 8px 0;
     font-size: 90%;
     font-weight: bold;
     padding: 5px;
 }
-table.hrdiff caption img, img.chora-stats {
+.diff-caption img {
     border: 1px solid #000;
-}
-table.hrdiff caption img {
     height: 10px;
     width: 10px;
     margin-left: 0.5em;
@@ -280,7 +275,7 @@ table.hrdiff caption img {
 
 div.difflog {
     padding: 2px;
-    margin: 5px;
+    margin: 0;
     border: 1px solid #ddd;
 }
 div.diffclear {
@@ -292,6 +287,10 @@ td.browseLocation em {
     display: block;
 }
 
+img.chora-stats {
+    border: 1px solid #000;
+}
+
 .historybg {
     background: #e9e9e9;
     text-align: center;