[pkg-horde] [SCM] Debian Horde Packages repository: chora2 package branch, debian-sid, updated. 6087b59a8a7d68af8bd357d44788b534d60bc52c

Gregory Colpart reg at foulademer.gcolpart.com
Sun May 31 15:31:18 UTC 2009


The following commit has been merged in the debian-sid branch:
commit 9a15604018cfd2b31dde30de7e62a43826baaf1f
Author: Gregory Colpart <reg at foulademer.gcolpart.com>
Date:   Sun May 31 16:06:10 2009 +0200

    merge from upstream

diff --git a/.htaccess b/.htaccess
index 9111f1a..2ace2c4 100644
--- a/.htaccess
+++ b/.htaccess
@@ -1,6 +1,7 @@
 <IfModule mod_rewrite.c>
 	RewriteEngine On
+	RewriteRule   ^([a-z]+).php/(.*)$ $1.php?f=$2 [QSA,L]
 	RewriteCond   %{REQUEST_FILENAME}  !-d
 	RewriteCond   %{REQUEST_FILENAME}  !-f
-	RewriteRule   ^(.*\/.*)$ browse.php\/$1 [QSA]
+	RewriteRule   ^(.*)$ browse.php?f=$1 [QSA]
 </IfModule>
diff --git a/COPYING b/COPYING
index 5a965fb..a6b6756 100644
--- a/COPYING
+++ b/COPYING
@@ -1,7 +1,7 @@
 		    GNU GENERAL PUBLIC LICENSE
 		       Version 2, June 1991
 
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ Copyright 1989, 1991 Free Software Foundation, Inc.
      59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  Everyone is permitted to copy and distribute verbatim copies
  of this license document, but changing it is not allowed.
diff --git a/README b/README
index c11d5ee..721b28b 100644
--- a/README
+++ b/README
@@ -1,8 +1,8 @@
 What is Chora?
 ==============
 
-:Last update:   $Date: 2006/07/27 12:23:50 $
-:Revision:      $Revision: 1.11.10.1 $
+:Last update:   $Date: 2008/10/09 17:40:02 $
+:Revision:      $Revision: 1.11.10.2 $
 :Contact:       chora at lists.horde.org
 
 .. contents:: Contents
diff --git a/annotate.php b/annotate.php
index cd1c65b..b1abaed 100644
--- a/annotate.php
+++ b/annotate.php
@@ -1,15 +1,18 @@
 <?php
 /**
- * $Horde: chora/annotate.php,v 1.48.8.5 2007/02/23 03:58:53 selsky Exp $
+ * $Horde: chora/annotate.php,v 1.48.8.7 2009/01/06 15:22:34 jan Exp $
  *
- * Copyright 2000-2007 Anil Madhavapeddy <anil at recoil.org>
+ * Copyright 2000-2009 The Horde Project (http://www.horde.org/)
  *
- * See the enclosed file COPYING for license information (GPL).  If you
+ * 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  Anil Madhavapeddy <avsm at horde.org>
  */
 
 @define('CHORA_BASE', dirname(__FILE__));
 require_once CHORA_BASE . '/lib/base.php';
+require_once 'Horde/Text/Filter.php';
 
 /* Spawn the file object. */
 $fl = &$VC->getFileObject($where, $cache);
@@ -18,14 +21,14 @@ Chora::checkError($fl);
 /* Retrieve the desired revision from the GET variable. */
 $rev = Util::getFormData('rev', '1.1');
 if (!VC_Revision::valid($rev)) {
-    Chora::fatal("Revision $rev not found");
+    Chora::fatal('404 Not Found', "Revision $rev not found");
 }
 
 $ann = &$VC->getAnnotateObject($fl);
 Chora::checkError($lines = $ann->doAnnotate($rev));
 
-$title = sprintf(_("Source Annotation of %s for version %s"), Text::htmlAllSpaces($where), $rev);
-$extraLink = sprintf('<a href="%s">%s</a> <b>|</b> <a href="%s">%s</a>',
+$title = sprintf(_("Source Annotation of %s (revision %s)"), Text::htmlAllSpaces($where), $rev);
+$extraLink = sprintf('<a href="%s">%s</a> | <a href="%s">%s</a>',
                      Chora::url('co', $where, array('r' => $rev)), _("View"),
                      Chora::url('co', $where, array('r' => $rev, 'p' => 1)), _("Download"));
 require CHORA_TEMPLATES . '/common-header.inc';
@@ -36,6 +39,17 @@ require CHORA_TEMPLATES . '/annotate/header.inc';
 $author = '';
 $style = 0;
 
+/* Map of revisions for finding the previous revision to a change. */
+$revMap = $fl->revs;
+sort($revMap);
+$rrevMap = array_flip($revMap);
+
+/* Keep track of any revision we encounter in the following loop. */
+$revList = array();
+
+/* Use this counter so that we can give each tooltip object a unique
+ * id attribute (which we use to set the tooltip text later). */
+$i = 0;
 foreach ($lines as $line) {
     $lineno = $line['lineno'];
     $prevAuthor = $author;
@@ -44,8 +58,16 @@ foreach ($lines as $line) {
         $style = (++$style % 2);
     }
     $rev = $line['rev'];
+    $prev = isset($revMap[$rrevMap[$rev] - 1]) ? $revMap[$rrevMap[$rev] - 1] : null;
+    if (!isset($revList[$rev])) {
+        $revList[$rev] = $fl->logs[$rev];
+    }
+    if (!is_null($prev) && !isset($revList[$prev])) {
+        $revList[$prev] = $fl->logs[$prev];
+    }
     $line = Text::htmlAllSpaces($line['line']);
     include CHORA_TEMPLATES . '/annotate/line.inc';
+    ++$i;
 }
 
 require CHORA_TEMPLATES . '/annotate/footer.inc';
diff --git a/browse.php b/browse.php
index 7a678ae..9293b1c 100644
--- a/browse.php
+++ b/browse.php
@@ -1,25 +1,28 @@
 <?php
 /**
- * $Horde: chora/browse.php,v 1.5.4.6 2007/02/23 03:58:53 selsky Exp $
+ * $Horde: chora/browse.php,v 1.5.4.8 2009/01/06 15:22:34 jan Exp $
  *
- * Copyright 1999-2007 Anil Madhavapeddy <anil at recoil.org>
- * Copyright 1999-2007 Charles Hagenbuch <chuck at horde.org>
+ * Copyright 1999-2009 The Horde Project (http://www.horde.org/)
  *
- * See the enclosed file COPYING for license information (GPL).  If you
+ * 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  Anil Madhavapeddy <anil at recoil.org>
+ * @author  Chuck Hagenbuch <chuck at horde.org>
+ * @package Chora
  */
 
 @define('CHORA_BASE', dirname(__FILE__));
 require_once CHORA_BASE . '/lib/base.php';
-require_once 'Horde/MIME/Magic.php';
-require_once 'Horde/MIME/Viewer.php';
-require_once HORDE_BASE . '/config/mime_drivers.php';
-require_once CHORA_BASE . '/config/mime_drivers.php';
+
+if (!$atdir && !$VC->isFile($fullname)) {
+    Chora::fatal('404 Not Found', "$where: no such file or directory");
+}
 
 if ($atdir) {
     Chora::checkError($dir = $VC->queryDir($where));
 
-    $atticFlags = (boolean)$acts['sa'];
+    $atticFlags = (bool)$acts['sa'];
     Chora::checkError($dir->browseDir($cache, true, $atticFlags));
     $dir->applySort($acts['sbt'], $acts['ord']);
     Chora::checkError($dirList = &$dir->queryDirList());
@@ -29,27 +32,27 @@ if ($atdir) {
     if ($where == '') {
         $title = $conf['options']['introTitle'];
     } else {
-        $title = sprintf(_("Source Directory of /%s"), Text::htmlallSpaces($where));
+        $title = sprintf(_("Source Directory of /%s"), $where);
     }
 
-    if (is_a($VC, 'VC_svn')) {
-        // Showing deleted files is not supported in svn.
-        $extraLink = '';
-    } else {
-        if ($acts['sa']) {
-            $extraLink = Horde::widget(Chora::url('', $where . '/', array('sa' => 0)), _("Hide Deleted Files"), 'widget', '', '', _("Hide _Deleted Files"));
-        } else {
-            $extraLink = Horde::widget(Chora::url('', $where . '/', array('sa' => 1)), _("Show Deleted Files"), 'widget', '', '', _("Show _Deleted Files"));
-        }
+    $extraLink = '';
+    if (is_a($VC, 'VC_cvs')) {
+        $extraLink = Horde::widget(Chora::url(
+            '', $where . '/', array('sa' => ($acts['sa'] ? 0 : 1))),
+            $acts['sa'] ? _("Hide Deleted Files") : _("Show Deleted Files"),
+            'widget', '', '',
+            $acts['sa'] ? _("Hide _Deleted Files") : _("Show _Deleted Files")
+        );
     }
 
-    require CHORA_TEMPLATES . '/common-header.inc';
-    require CHORA_TEMPLATES . '/menu.inc';
-    require CHORA_TEMPLATES . '/headerbar.inc';
+    $umap = array(
+        'age' => VC_SORT_AGE,
+        'rev' => VC_SORT_REV,
+        'name' => VC_SORT_NAME,
+        'author' => VC_SORT_AUTHOR
+    );
 
     foreach (array('age', 'rev', 'name', 'author') as $u) {
-        $umap = array('age' => VC_SORT_AGE, 'rev' => VC_SORT_REV,
-                      'name' => VC_SORT_NAME, 'author' => VC_SORT_AUTHOR);
         $arg = array('sbt' => $umap[$u]);
         if ($acts['sbt'] == $umap[$u]) {
             $arg['ord'] = !$acts['ord'];
@@ -59,186 +62,174 @@ if ($atdir) {
 
     /* Print out the directory header. */
     $printAllCols = count($fileList);
+
+    Horde::addScriptFile('prototype.js', 'chora', true);
+    Horde::addScriptFile('tables.js', 'chora', true);
+    require CHORA_TEMPLATES . '/common-header.inc';
+    require CHORA_TEMPLATES . '/menu.inc';
+    require CHORA_TEMPLATES . '/headerbar.inc';
     require CHORA_TEMPLATES . '/directory/header.inc';
 
     /* Unless we're at the top, display the 'back' bar. */
-    $dirrow = 1;
     if ($where != '') {
-        $dirrow = ++$dirrow % 2;
         $url = Chora::url('', preg_replace('|[^/]+$|', '', $where));
         require CHORA_TEMPLATES . '/directory/back.inc';
     }
 
     /* Display all the directories first. */
-    foreach ($dirList as $currentDir) {
-        if ($conf['hide_restricted'] && Chora::isRestricted($currentDir)) {
-            continue;
+    if ($dirList) {
+        echo '<tbody>';
+        foreach ($dirList as $currentDir) {
+            if ($conf['hide_restricted'] && Chora::isRestricted($currentDir)) {
+                continue;
+            }
+            $url = Chora::url('', "$where/$currentDir/");
+            $currDir = Text::htmlAllSpaces($currentDir);
+            require CHORA_TEMPLATES . '/directory/dir.inc';
         }
-        $dirrow = ++$dirrow % 2;
-        $url = Chora::url('', "$where/$currentDir/");
-        $currDir = Text::htmlAllSpaces($currentDir);
-        require CHORA_TEMPLATES . '/directory/dir.inc';
+        echo '</tbody>';
     }
 
     /* Display all of the files in this directory */
-    foreach ($fileList as $currFile) {
-        if ($conf['hide_restricted'] && Chora::isRestricted($currFile->queryName())) {
-            continue;
-        }
-        $dirrow = ++$dirrow % 2;
-        $lg = $currFile->queryLastLog();
-        if (is_a($lg, 'PEAR_Error')) {
-            continue;
-        }
-        $realname = $currFile->queryName();
-        $mimeType = MIME_Magic::filenameToMIME($realname);
-
-        $icon = MIME_Viewer::getIcon($mimeType);
-
-        $aid = $lg->queryAuthor();
-        $author = Chora::showAuthorName($aid);
-        $head = $currFile->queryHead();
-        $date = $lg->queryDate();
-        $log  = $lg->queryLog();
-        $attic = $currFile->isAtticFile();
-        $fileName = $where . ($attic ? '/' . 'Attic' : '') . '/' . $realname;
-        $name = Text::htmlAllSpaces($realname);
-        $url = Chora::url('', $fileName);
-        $readableDate = Chora::readableTime($date);
-        if ($log) {
-            $shortLog = str_replace("\n", ' ',
-                trim(substr($log, 0, $conf['options']['shortLogLength'] - 1)));
-            if (strlen($log) > 80) {
-                $shortLog .= "...";
+    if ($fileList) {
+        require_once 'Horde/MIME/Magic.php';
+        require_once 'Horde/MIME/Viewer.php';
+        if (is_callable(array('Horde', 'loadConfiguration'))) {
+            $result = Horde::loadConfiguration('mime_drivers.php', array('mime_drivers', 'mime_drivers_map'), 'horde');
+            extract($result);
+            $result = Horde::loadConfiguration('mime_drivers.php', array('mime_drivers', 'mime_drivers_map'), 'chora');
+            require_once 'Horde/Array.php';
+            if (isset($result['mime_drivers'])) {
+                $mime_drivers = Horde_Array::array_merge_recursive_overwrite($mime_drivers, $result['mime_drivers']);
+            }
+            if (isset($result['mime_drivers_map'])) {
+                $mime_drivers_map = Horde_Array::array_merge_recursive_overwrite($mime_drivers_map, $result['mime_drivers_map']);
             }
+        } else {
+            require_once HORDE_BASE . '/config/mime_drivers.php';
+            require_once CHORA_BASE . '/config/mime_drivers.php';
         }
-        require CHORA_TEMPLATES.'/directory/file.inc';
-    }
-    /* Display the options control panel at the bottom */
-    $formwhere = $scriptName . '/' . $where;
-
-    require CHORA_TEMPLATES . '/directory/footer.inc';
-    require $registry->get('templates', 'horde') . '/common-footer.inc';
-
-} elseif ($VC->isFile($fullname)) {
-    $fl = &$VC->getFileObject($where, $cache);
-    Chora::checkError($fl);
-    $title = sprintf(_("Source Log for %s"), Text::htmlAllSpaces($where));
-    $upwhere = preg_replace('|[^/]+$|', '', $where);
-    $onb = Util::getFormData('onb', 0);
-    $r1 = Util::getFormData('r1', 0);
-
-    $isBranch = isset($onb) && isset($fl->branches[$onb]) ? $fl->branches[$onb] : '';
-    $extraLink = Chora::getFileViews();
-
-    require CHORA_TEMPLATES . '/common-header.inc';
-    require CHORA_TEMPLATES . '/menu.inc';
-    require CHORA_TEMPLATES . '/headerbar.inc';
 
-    $mimeType = MIME_Magic::filenameToMIME($fullname);
-    $defaultTextPlain = ($mimeType == 'text/plain');
-
-    foreach ($fl->logs as $lg) {
-        $rev = $lg->rev;
-
-        /* Are we sticking only to one branch ? */
-        if ($onb && VC_Revision::valid($onb)) {
-
-            /* If so, if we are on the branch itself, let it through */
-            if (substr($rev, 0, strlen($onb)) != $onb) {
-
-                /* We are not on the branch, see if we are on a trunk
-                 * branch below the branch */
-                $baseRev = VC_Revision::strip($onb, 1);
-
-                /* Check we are at the same level of branching or less */
-                if (substr_count($rev, '.') <= substr_count($baseRev, '.')) {
-                    /* If we are at the same level, and the revision is
-                     * less, then let the revision through, since it was
-                     * committed before the branch actually took place
-                     */
-                    if (VC_Revision::cmp($rev, $baseRev) > 0) {
-                        continue;
-                    }
-                } else {
-                    continue;
+        echo '<tbody>';
+        foreach ($fileList as $currFile) {
+            if ($conf['hide_restricted'] &&
+                Chora::isRestricted($currFile->queryName())) {
+                continue;
+            }
+            $lg = $currFile->queryLastLog();
+            if (is_a($lg, 'PEAR_Error')) {
+                continue;
+            }
+            $realname = $currFile->queryName();
+            $mimeType = MIME_Magic::filenameToMIME($realname);
+
+            $icon = MIME_Viewer::getIcon($mimeType);
+
+            $author = Chora::showAuthorName($lg->queryAuthor());
+            $head = $currFile->queryHead();
+            $date = $lg->queryDate();
+            $log = $lg->queryLog();
+            $attic = $currFile->isAtticFile();
+            $fileName = $where . ($attic ? '/' . 'Attic' : '') . '/' . $realname;
+            $name = Text::htmlAllSpaces($realname);
+            $url = Chora::url('', $fileName);
+            $readableDate = Chora::readableTime($date);
+            if ($log) {
+                $shortLog = str_replace("\n", ' ',
+                    trim(substr($log, 0, $conf['options']['shortLogLength'] - 1)));
+                if (strlen($log) > 80) {
+                    $shortLog .= '...';
                 }
             }
+            require CHORA_TEMPLATES . '/directory/file.inc';
         }
+        echo '</tbody>';
+    }
 
-        $textURL = Chora::url('co', $where, array('r' => $rev));
-        $commitDate = strftime('%c', $lg->date);
-        $readableDate = Chora::readableTime($lg->date, true);
-
-        $aid = $lg->queryAuthor();
-        $author = Chora::showAuthorName($aid, true);
+    echo '</table>';
+    require $registry->get('templates', 'horde') . '/common-footer.inc';
+    exit;
+}
 
-        if (!empty($lg->tags)) {
-            $commitTags = implode(', ', $lg->tags);
-        } else {
-            $commitTags = '';
-        }
+/* Showing a file. */
+$fl = &$VC->getFileObject($where, $cache);
+Chora::checkError($fl);
+$title = sprintf(_("Revisions for %s"), $where);
+$onb = Util::getFormData('onb', 0);
+if (VC_Revision::valid($onb)) {
+    $onb_len = strlen($onb);
+    $onb_base = VC_Revision::strip($onb, 1);
+    $onb_parents = array();
+    while (substr_count($onb_base, '.')) {
+        $onb_parents[$onb_base] = true;
+        $onb_base = VC_Revision::strip($onb_base, 1);
+    }
+} else {
+    $onb = null;
+}
 
-        $branchPointsArr = array();
-        foreach ($lg->querySymbolicBranches() as $symb => $bra) {
-            $branchPointsArr[] = '<a href="' . Chora::url('', $where, array('onb' => $bra)) . '">'. $symb . '</a>';
-        }
+$extraLink = Chora::getFileViews();
+$first = end($fl->logs);
+$diffValueLeft = $first->queryRevision();
+$diffValueRight = $fl->queryRevision();
 
-        /* Calculate the current branch name and revision. */
-        $branchPoints = implode(' , ', $branchPointsArr);
-        $branchRev = VC_Revision::strip($rev, 1);
-        if (@isset($fl->branches[$branchRev])) {
-            $branchName = $fl->branches[$branchRev];
-        } else {
-            $branchName = '';
-        }
+$sel = '';
+foreach ($fl->symrev as $sm => $rv) {
+    $sel .= '<option value="' . $rv . '">' . $sm . '</option>';
+}
 
-        if ($prevRevision = $fl->queryPreviousRevision($lg->queryRevision())) {
-            $changedLines = $lg->queryChangedLines();
-            $coloredDiffURL = Chora::url('diff', $where, array('r1' => $prevRevision, 'r2' => $rev, 'ty' => 'h'));
-            $longDiffURL = Chora::url('diff', $where, array('r1' => $prevRevision, 'r2' => $rev, 'ty' => 'h', 'num' => 10));
-            $uniDiffURL = Chora::url('diff', $where, array('r1' => $prevRevision, 'r2' => $rev, 'ty' => 'u'));
-            $nowsDiffURL = Chora::url('diff', $where, array('ws' => 0, 'r1' => $prevRevision, 'r2' => $rev, 'ty' => 'u'));
-        }
+$selAllBranches = '';
+foreach ($fl->branches as $num => $sym) {
+    $selAllBranches .= '<option value="' . $num . '"' . ($num == $onb ? ' selected="selected"' : '') . '>' . $sym . '</option>';
+}
 
-        $manyRevisions = !($fl->queryRevision() === '1.1');
-        if ($manyRevisions) {
-            $selURL = Chora::url('', $where, array('r1' => $rev, 'onb' => $onb));
-            if (!empty($r1)) {
-                $selColoredDiffURL = Chora::url('diff', $where, array('r1' => $r1, 'r2' => $rev, 'ty' => 'h'));
-                $selLongDiffURL = Chora::url('diff', $where, array('r1' => $r1, 'r2' => $rev, 'ty' => 'h', 'num' => 10));
-                $selUniDiffURL = Chora::url('diff', $where, array('r1' => $r1, 'r2' => $rev, 'ty' => 'u'));
-                $selNowsDiffURL = Chora::url('diff', $where, array('ws' => 0, 'r1' => $r1, 'r2' => $rev, 'ty' => 'u'));
+Horde::addScriptFile('prototype.js', 'chora', true);
+Horde::addScriptFile('tables.js', 'chora', true);
+Horde::addScriptFile('QuickFinder.js', 'chora', true);
+Horde::addScriptFile('revlog.js', 'chora', true);
+require CHORA_TEMPLATES . '/common-header.inc';
+require CHORA_TEMPLATES . '/menu.inc';
+require CHORA_TEMPLATES . '/headerbar.inc';
+require CHORA_TEMPLATES . '/log/header.inc';
+
+$i = 0;
+foreach ($fl->logs as $lg) {
+    $rev = $lg->queryRevision();
+    list($branchName, $branchRev) = Chora::getBranch($fl, $rev);
+
+    /* Are we tracking a branch? */
+    if ($onb) {
+        /* If we are on the branch itself, let it through */
+        if (substr($rev, 0, $onb_len) != $onb) {
+            /* If the revision is on one of the parent branches, and
+             * is before the branch was made, let it through. */
+            if ((!isset($onb_parents[$branchRev]) && substr_count($rev, '.') > 1) ||
+                VC_Revision::cmp($rev, $onb) > 0) {
+                continue;
             }
         }
+    }
 
-        $logMessage = Chora::formatLogMessage($lg->log);
+    $textUrl = Chora::url('co', $where, array('r' => $rev));
+    $commitDate = Chora::formatDate($lg->queryDate());
+    $readableDate = Chora::readableTime($lg->queryDate(), true);
 
-        if ($r1 === $rev) {
-            $bgclass = 'diff-selected';
-        } else {
-            $bgclass = 'control';
-        }
+    $author = Chora::showAuthorName($lg->queryAuthor(), true);
+    $tags = Chora::getTags($lg, $where);
 
-        require CHORA_TEMPLATES . '/log/rev.inc';
+    if ($prevRevision = $fl->queryPreviousRevision($lg->queryRevision())) {
+        $diffUrl = Chora::url('diff', $where, array('r1' => $prevRevision, 'r2' => $rev));
+    } else {
+        $diffUrl = '';
     }
 
-    $first = end($fl->logs);
-    $diffValueLeft  = $first->rev;
-    $diffValueRight = $fl->queryRevision();
+    $logMessage = Chora::formatLogMessage($lg->queryLog());
 
-    $sel = '';
-    foreach ($fl->symrev as $sm => $rv) {
-        $sel .= '<option value="' . $rv . '">' . $sm . '</option>';
-    }
+    require CHORA_TEMPLATES . '/log/rev.inc';
 
-    $selAllBranches = '';
-    foreach ($fl->branches as $num => $sym) {
-        $selAllBranches .= '<option value="' . $num . '">' . $sym . '</option>';
+    if ($i++ > 100 && !Util::getFormData('all')) {
+        break;
     }
-
-    require CHORA_TEMPLATES . '/log/request.inc';
-    require $registry->get('templates', 'horde') . '/common-footer.inc';
-} else {
-    Chora::fatal("$where: no such file or directory");
 }
+require CHORA_TEMPLATES . '/log/footer.inc';
+require $registry->get('templates', 'horde') . '/common-footer.inc';
diff --git a/co.php b/co.php
index 4c4b699..63d3cc3 100644
--- a/co.php
+++ b/co.php
@@ -1,11 +1,13 @@
 <?php
 /**
- * $Horde: chora/co.php,v 1.59.2.5 2007/02/23 03:58:53 selsky Exp $
+ * $Horde: chora/co.php,v 1.59.2.7 2009/01/06 15:22:34 jan Exp $
  *
- * Copyright 2000-2007 Anil Madhavapeddy <anil at recoil.org>
+ * Copyright 2000-2009 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  Anil Madhavapeddy <avsm at horde.org>
  */
 
 @define('CHORA_BASE', dirname(__FILE__));
@@ -13,8 +15,21 @@ require_once CHORA_BASE . '/lib/base.php';
 require_once 'Horde/MIME/Part.php';
 require_once 'Horde/MIME/Magic.php';
 require_once 'Horde/MIME/Viewer.php';
-require_once HORDE_BASE . '/config/mime_drivers.php';
-require_once CHORA_BASE . '/config/mime_drivers.php';
+if (is_callable(array('Horde', 'loadConfiguration'))) {
+    $result = Horde::loadConfiguration('mime_drivers.php', array('mime_drivers', 'mime_drivers_map'), 'horde');
+    extract($result);
+    $result = Horde::loadConfiguration('mime_drivers.php', array('mime_drivers', 'mime_drivers_map'), 'chora');
+    require_once 'Horde/Array.php';
+    if (isset($result['mime_drivers'])) {
+        $mime_drivers = Horde_Array::array_merge_recursive_overwrite($mime_drivers, $result['mime_drivers']);
+    }
+    if (isset($result['mime_drivers_map'])) {
+        $mime_drivers_map = Horde_Array::array_merge_recursive_overwrite($mime_drivers_map, $result['mime_drivers_map']);
+    }
+} else {
+    require_once HORDE_BASE . '/config/mime_drivers.php';
+    require_once CHORA_BASE . '/config/mime_drivers.php';
+}
 
 /* If we know we're at a directory, just go to browse.php. */
 if ($atdir) {
@@ -43,7 +58,7 @@ if ($r == 0) {
 
 /* Is this a valid revision being requested? */
 if (!VC_Revision::valid($r)) {
-    Chora::fatal("Revision Not Found: $r is not a valid RCS revision number");
+    Chora::fatal('404 Not Found', "Revision Not Found: $r is not a valid revision number");
 }
 
 /* Retrieve the actual checkout. */
@@ -61,87 +76,52 @@ Chora::checkError($checkOut);
 
 if (!$plain) {
     /* Pretty-print the checked out copy */
-    $pretty = &Chora::pretty($mime_type, $checkOut);
-
-    $type = $pretty->getType();
-    if ((strpos($type, 'text/html') !== false || strpos($type, 'text/plain') !== false)
-        && $pretty->canDisplayInline()) {
-
-        $title = sprintf(_("Checkout of %s (revision %s)"), basename($fullname), $r);
-        $extraLink = sprintf('<a href="%s">%s</a> <b>|</b> <a href="%s">%s</a>',
-                             Chora::url('annotate', $where, array('rev' => $r)), _("Annotate"),
-                             Chora::url('co', $where, array('r' => $r, 'p' => 1)), _("Download"));
-
-        /* Make sure this revision exists. */
-        if (empty($file->logs[$r])) {
-            Chora::fatal(sprintf(_("Revision %s for file %s not found."), $r, $file));
-        }
-
-        /* Get this revision's attributes in printable form. */
-        $log = $file->logs[$r];
-        $commitDate = strftime('%c', $log->date);
-        $readableDate = Chora::readableTime($log->date, true);
-
-        $aid = $log->queryAuthor();
-        $author = Chora::showAuthorName($aid, true);
-
-        if (!empty($log->tags)) {
-            $commitTags = implode(', ', $log->tags);
-        } else {
-            $commitTags = '';
-        }
-
-        $branchPointsArr = array();
-        foreach ($log->querySymbolicBranches() as $symb => $bra) {
-            $branchPointsArr[] = '<a href="' . Chora::url('browse', $where, array('onb' => $bra)) . '">'. $symb . '</a>';
-        }
-
-        /* Calculate the current branch name and revision. */
-        $branchPoints = implode(' , ', $branchPointsArr);
-        $branchRev = VC_Revision::strip($r, 1);
-        if (@isset($fl->branches[$branchRev])) {
-            $branchName = $fl->branches[$branchRev];
-        } else {
-            $branchName = '';
-        }
-
-        if ($prevRevision = VC_Revision::prev($log->queryRevision())) {
-            $changedLines = $log->queryChangedLines();
-        }
-
-        $log_print = Chora::formatLogMessage($log->queryLog());
-        $i = 0;
-
-        require CHORA_TEMPLATES . '/common-header.inc';
-        require CHORA_TEMPLATES . '/menu.inc';
-        require CHORA_TEMPLATES . '/headerbar.inc';
-        require CHORA_TEMPLATES . '/checkout/checkout.inc';
-        require $registry->get('templates', 'horde') . '/common-footer.inc';
-    } else {
-        header('Content-Type: ' . $pretty->getType());
-        echo $pretty->render();
-    }
+    $pretty = Chora::pretty($mime_type, $checkOut);
+
+    /* Get this revision's attributes in printable form. */
+    $log = $file->logs[$r];
+
+    $title = sprintf(_("%s Revision %s (%s ago)"),
+                     basename($fullname),
+                     $r,
+                     Chora::readableTime($log->date, true));
+    $extraLink = sprintf('<a href="%s">%s</a> | <a href="%s">%s</a>',
+                         Chora::url('annotate', $where, array('rev' => $r)), _("Annotate"),
+                         Chora::url('co', $where, array('r' => $r, 'p' => 1)), _("Download"));
+
+    $tags = Chora::getTags($log, $where);
+    list($branchName,) = Chora::getBranch($file, $r);
+
+    $log_print = Chora::formatLogMessage($log->queryLog());
+    $author = Chora::showAuthorName($log->queryAuthor(), true);
+
+    Horde::addScriptFile('prototype.js', 'chora', true);
+    Horde::addScriptFile('stripe.js', 'chora', true);
+    require CHORA_TEMPLATES . '/common-header.inc';
+    require CHORA_TEMPLATES . '/menu.inc';
+    require CHORA_TEMPLATES . '/headerbar.inc';
+    require CHORA_TEMPLATES . '/checkout/checkout.inc';
+    require $registry->get('templates', 'horde') . '/common-footer.inc';
     exit;
-} else {
-    /* Download the file. */
-
-    // Get data.
-    $content = '';
-    while ($line = fgets($checkOut)) {
-        $content .= $line;
-    }
-    @fclose($checkOut);
+}
 
-    // Get name.
-    $filename = $file->queryName();
-    if ($browser->getBrowser() == 'opera') {
-        $filename = strtr($filename, ' ', '_');
-    }
+/* Download the file. */
 
-    // Send headers.
-    $browser->downloadHeaders($filename, $mime_type, false, strlen($content));
+// Get data.
+$content = '';
+while ($line = fgets($checkOut)) {
+    $content .= $line;
+}
+ at fclose($checkOut);
 
-    // Send data.
-    echo $content;
-    exit;
+// Get name.
+$filename = $file->queryName();
+if ($browser->getBrowser() == 'opera') {
+    $filename = strtr($filename, ' ', '_');
 }
+
+// Send headers.
+$browser->downloadHeaders($filename, $mime_type, false, strlen($content));
+
+// Send data.
+echo $content;
diff --git a/config/conf.xml b/config/conf.xml
index 0e097f5..493ba40 100644
--- a/config/conf.xml
+++ b/config/conf.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!-- $Horde: chora/config/conf.xml,v 1.8.10.2 2005/09/22 12:56:37 jan Exp $ -->
+<!-- $Horde: chora/config/conf.xml,v 1.8.10.3 2008/10/09 17:40:03 jan Exp $ -->
 <configuration>
 
  <configtab name="paths" desc="Paths and Locations">
@@ -17,6 +17,12 @@
    <configstring name="diff" desc="diff">/usr/bin/diff</configstring>
    <configstring name="svn" desc="svn">/usr/bin/svn</configstring>
 
+   <configstring name="svn_home" required="false" desc="Home directory for
+   execution of svn binary (specified via --config-dir to svn). This directory
+   must at least be readable to the webserver, otherwise svn will not work.
+   If you leave this empty, the system's temporary directory will be used.">
+   </configstring>
+
    <configstring name="cvsps" required="false" desc="If you have cvsps
    installed, we can generate patchset information. You need at least version
    2.0b6 of cvsps. Path to the cvsps executable, e.g. /usr/local/bin/cvsps"/>
@@ -39,11 +45,11 @@
    expression, or an array of regexps, which if matched, will link a string to
    a ticket-tracking/bug-tracking system. The replacement is the second
    argument to preg_replace(), so you can backreference anything you match in
-   the matching regexp. Example: &lt;code&gt;&lt;nobr&gt;'|bug:?
-   #?(\d+)|i'&lt;/nobr&gt;&lt;/code&gt;"/>
+   the matching regexp. Example: &lt;code class=&quot;nowrap&quot;&gt;'|bug:?
+   #?(\d+)|i'&lt;/code&gt;"/>
    <configstring name="replacement" required="false" desc="Replacement
-   string. Example: &lt;code&gt;&lt;nobr&gt;'&amp;lt;a
-   href=&quot;http://bugs.example.com/show_bug.cgi?id=\1&quot;&amp;gt;\0&amp;lt;/a&amp;gt;'&lt;/nobr&gt;&lt;/code&gt;"/>
+   string. Example: &lt;code class=&quot;nowrap&quot;&gt;'&amp;lt;a
+   href=&quot;http://bugs.example.com/show_bug.cgi?id=\1&quot;&amp;gt;\0&amp;lt;/a&amp;gt;'&lt;/code&gt;"/>
   </configsection>
  </configtab>
 
@@ -69,26 +75,32 @@
      <value desc="sort by author name">VC_SORT_AUTHOR</value>
     </values>
    </configenum>
-   <configenum name="urls" desc="Does your web server support the PATH_INFO
-   method of passing URL data (some Windows web servers do not)?  If this
-   option is unchecked, pathnames will be propagated using a GET variable
-   instead.">get
+   <configenum name="urls" desc="Does your web server support
+   mod_rewrite?  If so, we can generate &quot;pretty&quot; URLs. If
+   not, pathnames will be propagated using a GET variable instead.">get
     <values>
      <value desc="GET (will always work)">get</value>
-     <value desc="PATH_INFO (somewhat prettier URLs)">path_info</value>
-     <value desc="mod_rewrite (prettiest, shortest URLs)">rewrite</value>
+     <value desc="mod_rewrite (pretty, shorter URLs)">rewrite</value>
     </values>
    </configenum>
   </configsection>
 
   <configlist name="restrictions" required="false" desc="If you wish to protect
-   a file pattern on a global basis (i.e. across all sourceroots defined in
-   sourceroots.php) list the perl-style regex file patterns in this array. For
-   example: &lt;code&gt;&lt;nobr&gt;'^/?CVSROOT'&lt;/nobr&gt;&lt;/code&gt;"/>
+  a file pattern on a global basis (i.e. across all sourceroots defined in
+  sourceroots.php) list the perl-style regex file patterns in this array. For
+  example: &lt;code class=&quot;nowrap&quot;&gt;'^/?CVSROOT'&lt;/code&gt;"/>
 
   <configboolean name="hide_restricted" desc="If you wish to hide restricted
-  files, check this option, and restricted files will not be
-  displayed.">true</configboolean>
+  files from listings as well as protect their contents, check this option, and
+  restricted files will not be displayed in directory
+  lists.">true</configboolean>
+
+  <configenum name="filename_linkto" desc="When clicking on a file name in the browse view, should we show the revision log, or display the most recent revision? The opposite action will be available when clicking on the revision number.">revlog
+   <values>
+    <value desc="Revision Log">revlog</value>
+    <value desc="Most recent version">checkout</value>
+   </values>
+  </configenum>
  </configtab>
 
  <configtab name="menu" desc="Menu Settings">
diff --git a/config/cvsgraph.conf.dist b/config/cvsgraph.conf.dist
index c324b7d..318f872 100644
--- a/config/cvsgraph.conf.dist
+++ b/config/cvsgraph.conf.dist
@@ -1,6 +1,6 @@
 # CvsGraph configuration
 #
-# $Horde: chora/config/cvsgraph.conf.dist,v 1.1 2003/06/10 01:29:50 mikec Exp $
+# $Horde: chora/config/cvsgraph.conf.dist,v 1.1.12.1 2008/10/09 17:40:03 jan Exp $
 #
 # - Empty lines and whitespace are ignored.
 #
@@ -87,11 +87,11 @@ date_format     = "%d-%b-%Y %H:%M:%S";
 box_shadow      = true;
 
 tag_font        = medium;
-tag_color       = "#007000";
+tag_color       = "#333399";
 
 rev_font        = giant;
 rev_color       = "#000000";
-rev_bgcolor     = "#f0f0f0";
+rev_bgcolor     = "#e9e9e9";
 rev_separator   = 1;
 rev_minline     = 15;
 rev_maxline     = 30;
@@ -101,7 +101,7 @@ rev_tspace      = 3;
 rev_bspace      = 3;
 rev_text        = "%d\n%a, %s"; # or "%d" for just the date
 rev_text_font   = tiny;
-rev_text_color  = "#500020";
+rev_text_color  = "#000000";
 
 # branch_font <number>
 #   The font of the number and tags
@@ -113,9 +113,9 @@ rev_text_color  = "#500020";
 #   Exterior spacing
 # branch_connect <number>
 #   Length of the vertical connector
-branch_font     = medium;
-branch_color    = "#0000c0";
-branch_bgcolor  = "#ffffc0";
+branch_font     = giant;
+branch_color    = "#333399";
+branch_bgcolor  = "#ffffff";
 branch_lspace   = 5;
 branch_rspace   = 5;
 branch_tspace   = 3;
@@ -134,12 +134,12 @@ branch_connect  = 8;
 #   1 = center
 #   2 = right
 # title_color <color>
-title           = "%C: %p%F\nRevisions: %r, Branches: %b";
+title           = "%p%F\nRevisions: %r, Branches: %b";
 title_x         = 10;
 title_y         = 5;
 title_font      = small;
 title_align     = left;
-title_color     = "#800000";
+title_color     = "#000000";
 
 # Margins of the image
 # Note: the title is outside the margin
diff --git a/config/longIntro.txt.dist b/config/longIntro.txt.dist
index d3d6203..ab3a003 100644
--- a/config/longIntro.txt.dist
+++ b/config/longIntro.txt.dist
@@ -9,5 +9,5 @@ display diffs between arbitrary revisions.
 </p>
 <p>
 More information on accessing the Horde CVS repository is available here:
-<a href="http://horde.org/source/">http://horde.org/source/</a>
+<a href="http://www.horde.org/source/">http://www.horde.org/source/</a>
 </p>
diff --git a/config/mime_drivers.php.dist b/config/mime_drivers.php.dist
index 01aa690..a1aa6ed 100644
--- a/config/mime_drivers.php.dist
+++ b/config/mime_drivers.php.dist
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * $Horde: chora/config/mime_drivers.php.dist,v 1.5 2004/10/20 05:35:03 slusarz Exp $
+ * $Horde: chora/config/mime_drivers.php.dist,v 1.5.10.1 2008/10/09 17:40:03 jan Exp $
  *
  * Decide which output drivers you want to activate for the Chora application.
  *
diff --git a/config/prefs.php.dist b/config/prefs.php.dist
index 010e519..3a84b35 100644
--- a/config/prefs.php.dist
+++ b/config/prefs.php.dist
@@ -1,6 +1,6 @@
 <?php
 /**
- * $Horde: chora/config/prefs.php.dist,v 1.3 2003/11/18 20:28:11 slusarz Exp $
+ * $Horde: chora/config/prefs.php.dist,v 1.3.12.1 2008/10/09 17:40:03 jan Exp $
  *
  * See horde/config/prefs.php for documentation on the structure of this file.
  */
diff --git a/config/sourceroots.php.dist b/config/sourceroots.php.dist
index 9cf3bf3..febe066 100644
--- a/config/sourceroots.php.dist
+++ b/config/sourceroots.php.dist
@@ -1,6 +1,6 @@
 <?php
 /*
- * $Horde: chora/config/sourceroots.php.dist,v 1.2 2003/08/27 14:08:21 chuck Exp $
+ * $Horde: chora/config/sourceroots.php.dist,v 1.2.12.2 2008/10/09 17:40:03 jan Exp $
  *
  * This file contains all the configuration information for the
  * various CVS repositories that you wish to display. You should have
@@ -11,7 +11,7 @@
  * 'name'     [M] : Short name for the repository.
  *
  * 'location' [M] : Location on the filesystem of the repository.
- * 
+ *
  * 'title'    [M] : Long title for the repository.
  *
  * 'type'     [M] : Indicates software used, either 'cvs' or 'svn'.
@@ -29,6 +29,12 @@
  *
  * 'restrictions' : Array of perl-style regular expressions for those files
  *                  whose contents should be protected and not displayed.
+ *
+ * 'username'     : Username to use for the repository (only for Subversion
+ *                  repositories that require authentication).
+ *
+ * 'password'     : Password to use for the repository (only for Subversion
+ *                  repositories that require authentication).
  */
 
 $sourceroots['php4'] = array(
diff --git a/cvs.php b/cvs.php
index 58e3be0..8e6ca5b 100644
--- a/cvs.php
+++ b/cvs.php
@@ -1,12 +1,13 @@
 <?php
 /**
- * $Horde: chora/cvs.php,v 1.180.10.4 2007/01/02 13:54:04 jan Exp $
+ * $Horde: chora/cvs.php,v 1.180.10.6 2009/01/06 15:22:34 jan Exp $
  *
- * Copyright 1999-2007 Anil Madhavapeddy <anil at recoil.org>
- * Copyright 1999-2007 Charles Hagenbuch <chuck at horde.org>
+ * Copyright 1999-2009 The Horde Project (http://www.horde.org/)
  *
- * See the enclosed file COPYING for license information (GPL).  If you
+ * See the enclosed file COPYING for license information (GPL). If you
  * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @package Chora
  */
 
 // The functionality in this script has moved to browse.php. This file
diff --git a/cvsgraph.php b/cvsgraph.php
index 3723089..bfdc6e6 100644
--- a/cvsgraph.php
+++ b/cvsgraph.php
@@ -1,14 +1,17 @@
 <?php
 /**
- * $Horde: chora/cvsgraph.php,v 1.15.8.4 2007/02/23 03:58:53 selsky Exp $
- *
  * Wrapper for CVSGraph.
  *
- * Copyright 1999-2007 Anil Madhavapeddy <anil at recoil.org>
- * Copyright 1999-2007 Charles Hagenbuch <chuck at horde.org>
+ * $Horde: chora/cvsgraph.php,v 1.15.8.6 2009/01/06 15:22:34 jan Exp $
+ *
+ * Copyright 1999-2009 The Horde Project (http://www.horde.org/)
  *
- * See the enclosed file COPYING for license information (GPL).  If you
+ * 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  Anil Madhavapeddy <anil at recoil.org>
+ * @author  Chuck Hagenbuch <chuck at horde.org>
+ * @package Chora
  */
 
 @define('CHORA_BASE', dirname(__FILE__));
@@ -31,7 +34,7 @@ if (@is_file($fullname . ',v')) {
 
         // Build up the argument string.
         $argstr = '';
-        if (substr(PHP_OS, 0, 3) == 'WIN') {
+        if (!strncasecmp(PHP_OS, 'WIN', 3)) {
             foreach ($args as $key => $val) {
                 $argstr .= "-$key \"$val\" ";
             }
@@ -46,7 +49,6 @@ if (@is_file($fullname . ',v')) {
     } else {
         // Display the wrapper page for the image.
         $title = sprintf(_("Graph for %s"), Text::htmlAllSpaces($where));
-        $upwhere = preg_replace('|[^/]+$|', '', $where);
         $extraLink = Chora::getFileViews();
 
         require CHORA_TEMPLATES . '/common-header.inc';
@@ -66,7 +68,7 @@ if (@is_file($fullname . ',v')) {
 
         // Build up the argument string.
         $argstr = '';
-        if (substr(PHP_OS, 0, 3) == 'WIN') {
+        if (!strncasecmp(PHP_OS, 'WIN', 3)) {
             foreach ($args as $key => $val) {
                 $argstr .= "-$key \"$val\" ";
             }
@@ -83,5 +85,5 @@ if (@is_file($fullname . ',v')) {
         require $registry->get('templates', 'horde') . '/common-footer.inc';
     }
 } else {
-    Chora::fatal("$where: no such file or directory");
+    Chora::fatal('404 Not Found', "$where: no such file or directory");
 }
diff --git a/diff.php b/diff.php
index d871d40..e4fa2e0 100644
--- a/diff.php
+++ b/diff.php
@@ -1,197 +1,180 @@
 <?php
 /**
- * $Horde: chora/diff.php,v 1.87.8.3 2007/01/02 13:54:05 jan Exp $
+ * $Horde: chora/diff.php,v 1.87.8.5 2009/01/06 15:22:34 jan Exp $
  *
- * Copyright 2000-2007 Anil Madhavapeddy <anil at recoil.org>
+ * Copyright 2000-2009 The Horde Project (http://www.horde.org/)
  *
- * See the enclosed file COPYING for license information (GPL).  If you
+ * 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  Anil Madhavapeddy <avsm at horde.org>
  */
 
 @define('CHORA_BASE', dirname(__FILE__));
 require_once CHORA_BASE . '/lib/base.php';
+require_once 'Horde/MIME/Magic.php';
 
 /* Spawn the repository and file objects */
 $fl = &$VC->getFileObject($where, $cache);
 Chora::checkError($fl);
 
-/* Initialise the form variables correctly.
- * If r1/r2 are empty, then use the corresponding text field instead */
-$r1 = Util::getFormData('r1', 0);
-$r2 = Util::getFormData('r2', 0);
-
-if (!$r1) {
-    $r1 = Util::getFormData('tr1');
-}
-if (!$r2) {
-    $r2 = Util::getFormData('tr2');
+/* Initialise the form variables correctly. */
+$r1 = Util::getFormData('r1');
+$r2 = Util::getFormData('r2');
+
+/* If we are in a SVN repository (or another VC system that doesn't
+ * always use consecutive revision numbers from change-to-change
+ * per-file) and we compare against an older version that does not
+ * exist, then we search for the newest version older than the given
+ * old version. */
+if (!isset($fl->logs[$r1]) && isset($fl->logs[$r2]) && $r1 < $r2) {
+    $rn = 0;
+    foreach (array_keys($fl->logs) as $r) {
+        if ($r > $rn && $r < $r1) {
+            $rn = $r;
+        }
+    }
+    if ($rn) {
+        $r1 = $rn;
+    }
 }
 
-/* If no context-size has been specified, default to 3. */
-$num = (int)Util::getFormData('num', 3);
+/* Ensure that we have valid revision numbers. */
+if (!VC_Revision::valid($r1) || !VC_Revision::valid($r2)) {
+    Chora::fatal('403 Forbidden', _("Malformed Query"));
+}
 
 /* If no type has been specified, then default to human readable. */
-$ty = Util::getFormData('ty', 'h');
-
-/* Unless otherwise specified, show whitespace differences. */
-$ws = Util::getFormData('ws', 1);
-
-/* Figure out what type of diff has been requested. */
-switch ($ty) {
-case 's':
-    $type = 'column';
-    break;
-
-case 'c':
-    $type = 'context';
-    break;
-
-case 'e':
-    $type = 'ed';
-    break;
-
-case 'u':
-case 'h':
-default:
+$type = Util::getFormData('t', 'colored');
+if (Util::getFormData('ty') == 'u') {
     $type = 'unified';
 }
 
-/* Ensure that we have valid revision numbers. */
-if (!VC_Revision::valid($r1) || !VC_Revision::valid($r2)) {
-    Chora::fatal(_("Malformed Query"));
-}
+/* Unless otherwise specified, show whitespace differences and 3 lines
+ * of context. */
+$ws = Util::getFormData('ws', 1);
+$num = (int)Util::getFormData('num', 3);
 
 /* Cache the output of the diff for a week - it can be longer, since
- * it should never change */
+ * it should never change. */
 header('Cache-Control: max-age=604800');
 
-/* Title to use for html output pages */
+/* All is ok, proceed with the diff. Always make sure there is a newline at
+ * the end of the file - patch requires it. */
+if ($type != 'colored') {
+    header('Content-Type: text/plain');
+    echo implode("\n", $VC->getDiff($fl, $r1, $r2, $type, $num, $ws)) . "\n";
+    exit;
+}
+
+/* Human-Readable diff. */
 $title = sprintf(_("Diff for %s between version %s and %s"),
                  Text::htmlallspaces($where), $r1, $r2);
 
-/* All is ok, proceed with the diff */
-switch ($type) {
-case 'column':
-    /* We'll need to know the mime type to modify diffs based on the mime
-       type. */
-    require_once 'Horde/MIME/Magic.php';
-    $mime_type = MIME_Magic::filenameToMIME($fullname);
-
-    if ($browser->isViewable($mime_type)) {
-        // The above are images that most web browsers can inline
-        // We borrow a *large* part of this from the Human-Readable case
-        $url1 = Chora::url('co', $where, array('r' => $r1));
-        $url2 = Chora::url('co', $where, array('r' => $r2));
-        $path = $fl->queryModulePath();
-
-        // Get the log entry so we can display it
-        $log = $fl->logs[$r2];
-        $log_print = Chora::formatLogMessage($log->queryLog());
-
-        // Start the html output, include menu bar and headers.
-        require CHORA_TEMPLATES . '/common-header.inc';
-        require CHORA_TEMPLATES . '/menu.inc';
-        require CHORA_TEMPLATES . '/headerbar.inc';
-
-        // Create a table for the two revisions, display log, and
-        // print a labeled bar for the revisions.
-        require CHORA_TEMPLATES . '/diff/hr/header.inc';
-        echo "<td><img src=\"$url1\"></td>";
-        echo "<td><img src=\"$url2\"></td>";
-        echo '</tr>';
-        require $registry->get('templates', 'horde') . '/common-footer.inc';
-    } else {
-        header('Content-Type: text/plain');
-        echo implode("\n", $VC->getDiff($fl, $r1, $r2, $type, $num, $ws));
+/* Format log entries. */
+$log = &$fl->logs;
+$log_messages = array();
+$range = Chora::getRevisionRange($r1, $r2);
+foreach ($range as $val) {
+    list($branchname, $branchrev) = Chora::getBranch($fl, $val);
+    if (isset($log[$val])) {
+        $clog = &$log[$val];
+        $log_messages[] = array(
+            'rev' => $val,
+            'msg' => Chora::formatLogMessage($clog->queryLog()),
+            'author' => Chora::showAuthorName($clog->queryAuthor(), true),
+            'branchRev' => $branchrev,
+            'branchName' => $branchname,
+            'date' => Chora::formatDate($clog->queryDate()),
+            'tags' => Chora::getTags($clog, $where),
+        );
     }
-    break;
-
-case 'context':
-    header('Content-Type: text/plain');
-    echo implode("\n", $VC->getDiff($fl, $r1, $r2, $type, $num, $ws));
-    break;
+}
 
-case 'ed':
-    header('Content-Type: text/plain');
-    echo implode("\n", $VC->getDiff($fl, $r1, $r2, $type, $num, $ws));
-    break;
-
-case 'unified':
-default:
-    if ($ty != 'h') {
-        /* Not Human-Readable format. */
-        header('Content-Type: text/plain');
-        echo implode("\n", $VC->getDiff($fl, $r1, $r2, $type, $num, $ws));
+Horde::addScriptFile('prototype.js', 'chora', true);
+Horde::addScriptFile('stripe.js', 'chora', true);
+require CHORA_TEMPLATES . '/common-header.inc';
+require CHORA_TEMPLATES . '/menu.inc';
+require CHORA_TEMPLATES . '/headerbar.inc';
+require CHORA_TEMPLATES . '/diff/hr/header.inc';
+
+$mime_type = MIME_Magic::filenameToMIME($fullname);
+if (substr($mime_type, 0, 6) == 'image/') {
+    /* Check for images. */
+    $url1 = Chora::url('co', $where, array('r' => $r1, 'p' => 1));
+    $url2 = Chora::url('co', $where, array('r' => $r2, 'p' => 1));
+
+    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::humanReadable($VC->getDiff($fl, $r1, $r2, 'unified', $num, $ws));
+    if (!$lns) {
+        /* Is the diff empty? */
+        require CHORA_TEMPLATES . '/diff/hr/nochange.inc';
     } else {
-        /* Human-Readable diff. */
-
-        /* Output standard header information for the page. */
-        $filename = preg_replace('/^.*\//', '', $where);
-        $pathname = preg_replace('/[^\/]*$/', '', $where);
-
-        $log = $fl->logs[$r2];
-        $log_print = Chora::formatLogMessage($log->queryLog());
+        /* 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';
+                }
 
-        require CHORA_TEMPLATES . '/common-header.inc';
-        require CHORA_TEMPLATES . '/menu.inc';
-        require CHORA_TEMPLATES . '/headerbar.inc';
-        require CHORA_TEMPLATES . '/diff/hr/header.inc';
+                switch ($change['type']) {
+                case 'add':
+                    $line = '';
+                    foreach ($change['lines'] as $l) {
+                        $line .= htmlspecialchars($l) . '<br />';
+                    }
+                    require CHORA_TEMPLATES . '/diff/hr/add.inc';
+                    break;
 
-        /* Retrieve the tree of changes. */
-        $lns = VC_Diff::humanReadable($VC->getDiff($fl, $r1, $r2, 'unified', $num, $ws));
-        /* TODO: check for errors here (PEAR_Error returned) - avsm */
-        /* Is the diff empty? */
-        if (!sizeof($lns)) {
-            require CHORA_TEMPLATES . '/diff/hr/nochange.inc';
-        } else {
-            /* Iterate through every header block of changes */
-            foreach ($lns as $header) {
-                $lefthead = Text::htmlspaces(@$header['oldline']);
-                $righthead = Text::htmlspaces(@$header['newline']);
-                $headfunc = Text::htmlspaces(@$header['function']);
-                require CHORA_TEMPLATES . '/diff/hr/row.inc';
-
-                /* Each header block consists of a number of changes
-                 * (add, remove, change) */
-                foreach ($header['contents'] as $change) {
-                    switch ($change['type']) {
-                    case 'add':
-                        foreach ($change['lines'] as $line) {
-                            $line = Text::htmlAllSpaces($line);
-                            require CHORA_TEMPLATES . '/diff/hr/add.inc';
-                        }
-                        break;
-
-                    case 'remove':
-                        foreach ($change['lines'] as $line) {
-                            $line = Text::htmlAllSpaces($line);
-                            require CHORA_TEMPLATES . '/diff/hr/remove.inc';
-                        }
-                        break;
-
-                    case 'empty':
-                        $line = Text::htmlAllSpaces($change['line']);
-                        require CHORA_TEMPLATES . '/diff/hr/empty.inc';
-                        break;
-
-                    case 'change':
-                        /* Pop the old/new stacks one by one, until
-                         * both are empty. */
-                        $oldsize = count($change['old']);
-                        $newsize = count($change['new']);
-                        for ($row = 0; $row < max($oldsize, $newsize); $row++) {
-                            $left = isset($change['old'][$row]) ? Text::htmlAllSpaces($change['old'][$row]) : '';
-                            $right = isset($change['new'][$row]) ? Text::htmlAllSpaces($change['new'][$row]) : '';
-                            require CHORA_TEMPLATES . '/diff/hr/change.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;
                 }
             }
-        }
 
-        /* Print legend. */
-        require CHORA_TEMPLATES . '/diff/hr/footer.inc';
-        require $registry->get('templates', 'horde') . '/common-footer.inc';
+            if (!empty($curContext)) {
+                $line = $curContext;
+                $curContext = '';
+                require CHORA_TEMPLATES . '/diff/hr/empty.inc';
+            }
+        }
     }
 }
+
+require CHORA_TEMPLATES . '/diff/hr/footer.inc';
+require $registry->get('templates', 'horde') . '/common-footer.inc';
diff --git a/docs/CHANGES b/docs/CHANGES
index 234a040..40ac258 100644
--- a/docs/CHANGES
+++ b/docs/CHANGES
@@ -1,3 +1,36 @@
+----
+v2.1
+----
+
+[jan] Add Italian translation (Fabio Pedretti <fabio.pedretti at ing.unibs.it>).
+[cjh] Allow configuring which views the file name and latest revision number
+      link to (jon at spriggs.org.uk, Request #6690).
+[mms] Add log entries to diff screen (Request #6193).
+[cjh] Apply fix for http://dev.rubyonrails.org/ticket/11473 to prototype.js
+      (Request #6590).
+[cjh] With Horde 3.2+, a username and password can be supplied for
+      authenticating to Subversion repositories (duck at obala.net,
+      Request #5958).
+[cjh] Use a dropdown list to show alternate code repositories
+      (greg_swift at aotx.uscourts.gov, Request #5600).
+[cjh] Add the ability to walk backwards to the next oldest revision when
+      diffing on a VC system where the revision numbers of a single file
+      are not always consecutive (helly at php.net, Request #5350).
+[jan] Add configuration to specify temporary directory for SVN (requires Horde
+      3.2, meyer at mesw.de, Request #5051).
+[cjh] Include a link to the previous revision in annotation views
+      (Request #197).
+[cjh] Make the revision log searchable and sortable.
+[cjh] Only show the 100 latest revisions by default (Bug #2584).
+[cjh] Show commit logs when hovering on the revision number in the
+      annotate view (robzilla at siklos.ca, Bug #3076).
+[cjh] Simplify codepaths by supporting only GET or mod_rewrite URLs.
+[jan] Add permissions per repository (ben at alkaloid.net, Request #3186).
+[cjh] Add support for dynamic re-sorting of file lists.
+[mas] Change any output of <b> and <i> tags to <strong> and <em> for better
+      accessibility support.
+
+
 ------
 v2.0.2
 ------
@@ -172,7 +205,7 @@ v0.6.5-dev
 ----------
 
 [avsm] Diff selection from the log view.
-       (Mathieu Arnold <arn_mat at club-internet.fr>)
+       (Mathieu Arnold <mat at mat.cc>)
 [avsm] Improve cvsusers parsing for varied email addresses.
 [avsm] Use the Horde Cache in the file log view to speed
        things up.
@@ -226,11 +259,11 @@ v0.4-dev
 [cjh]  Start using the Horde framework and complying more with
        CODING_STANDARDS.
 [cjh]  A number of fixes for files and directories with spaces in their
-       names (Mathieu Arnold <arn_mat at club-internet.fr>).
+       names (Mathieu Arnold <mat at mat.cc>).
 [cjh]  Add long unified diff option (Mathieu Arnold
-       <arn_mat at club-internet.fr>).
+       <mat at mat.cc>).
 [cjh]  Add default sort direction configuration option (Mathieu Arnold
-       <arn_mat at club-internet.fr>).
+       <mat at mat.cc>).
 [cjh]  Fix bug with Chora in the webroot (http://cvs.example.com/).
 [avsm] Overhaul CVSLib_Checkout interface to be in line with
        the rest of the CVSLib_* API (and much simpler).
diff --git a/docs/CREDITS b/docs/CREDITS
index ec7f6bd..5094838 100644
--- a/docs/CREDITS
+++ b/docs/CREDITS
@@ -34,6 +34,7 @@ Finnish                 Tero Matinlassi <tero.matinlassi at edu.vantaa.fi>
                         Leena Heino <leena.heino at uta.fi>
 French                  Mathieu Arnold <mat at absolight.net>
 German                  Jan Schneider <jan at horde.org>
+Italian                 Fabio Pedretti <fabio.pedretti at ing.unibs.it>
 Norwegian Bokmaal       Oystein Steimler <oystein at rexta.net>
 Polish                  Piotr Roszatycki <Piotr_Roszatycki at netia.net.pl>
 Romanian                Eugen Hoanca <eugenh at urban-grafx.ro>
@@ -42,5 +43,5 @@ Russian                 Illya Belov <belov at iop.irkps.ru>
 Slovak                  Ivan Noris <vix at vazka.sk>
 Spanish                 Julian Jares <jjares at techie.com>
                         Manuel Perez Ayala <mperaya at alcazaba.unex.es>
-Swedish                 Andreas Dahlén <andreas at dahlen.ws>
+Swedish                 Andreas Dahlén <andreas at dahlen.ws>
 =====================   ======================================================
diff --git a/docs/INSTALL b/docs/INSTALL
index 72dff73..f610143 100644
--- a/docs/INSTALL
+++ b/docs/INSTALL
@@ -2,8 +2,8 @@
  Installing Chora 2.0
 ======================
 
-:Last update:   $Date: 2005/02/03 15:04:39 $
-:Revision:      $Revision: 1.7.4.1 $
+:Last update:   $Date: 2008/10/09 17:40:04 $
+:Revision:      $Revision: 1.7.4.2 $
 :Contact:       chora at lists.horde.org
 
 .. contents:: Contents
@@ -168,9 +168,8 @@ subscription information can be found at
 
   http://www.horde.org/mail/
 
-Lastly, Horde developers, contributors and users also make occasional
-appearances on IRC, on the channel #horde on the freenode Network
-(irc.freenode.net).
+Lastly, Horde developers, contributors and users may also be found on IRC,
+on the channel #horde on the Freenode Network (irc.freenode.net).
 
 Please keep in mind that Chora is free software written by volunteers.  For
 information on reasonable support expectations, please read
diff --git a/docs/RELEASE_NOTES b/docs/RELEASE_NOTES
index eef7767..4381956 100644
--- a/docs/RELEASE_NOTES
+++ b/docs/RELEASE_NOTES
@@ -12,29 +12,36 @@
  * 8 - Minor security fixes
  * 9 - Major security fixes
  */
-$this->notes['fm']['focus'] = 4;
+$this->notes['fm']['focus'] = 5;
 
 /* Mailing list release notes. */
 $this->notes['ml']['changes'] = <<<ML
 The Horde Team is pleased to announce the final release of the Chora
-Repository Viewer version H3 (2.0.2).
+Repository Viewer version H3 (2.1).
 
 Chora is built upon the Horde Application Framework and provides a read-only
 browser interface to any number of version control repositories.  Advanced
 features include a visual branch view of the repository's history,
 pretty-printed output, annotation, patchsets and basic statistics.
 
-Major changes compared to the Chora version H3 (2.0.1) are:
-    * Changed sort order of patchsets to match commit logs.
-    * Added Danish translation.
-    * Updated almost all translations.
+The major changes compared to the Chora H3 (2.0) versions are:
+    * Log entries are shown for each revision in a diff
+    * Links to the previous revision in the annotate view
+    * Multiple repositories are shown in a dropdown menu.
+    * Dynamic sorting in browse views, and dynamic sorting and
+      searching in the history view.
+    * Control access to each repository with Horde Permissions
+    * Authentication for subversion repositories
+    * Performance improvements
+    * Better accessibility
 ML;
 
 /* Freshmeat release notes. */
 $this->notes['fm']['changes'] = <<<FM
-The sort order of patchsets has been changed to match commit log ordering.
-A Danish translation has been added and almost all other translations have
-been updated.
+This release includes more information in annotate and diff views, adds dynamic
+sorting and searching of information in the browse and history views, allows
+controlling permissions on individual repositories, and adds better performance
+and accessibility than the previous version.
 FM;
 
 $this->notes['name'] = 'Chora';
diff --git a/docs/TODO b/docs/TODO
index 76d1cbc..8c4a309 100644
--- a/docs/TODO
+++ b/docs/TODO
@@ -2,8 +2,8 @@
  Chora Development TODO List
 =============================
 
-:Last update:   $Date: 2004/09/28 15:18:26 $
-:Revision:      $Revision: 1.5 $
+:Last update:   $Date: 2008/10/09 17:40:04 $
+:Revision:      $Revision: 1.5.10.1 $
 :Contact:       chora at lists.horde.org
 
 - Other utility support (lint support for C files, pod2html for perl, PHPDoc
diff --git a/history.php b/history.php
index e47f6b0..1aab152 100644
--- a/history.php
+++ b/history.php
@@ -1,44 +1,43 @@
 <?php
 /**
- * $Horde: chora/history.php,v 1.53.8.4 2007/01/02 13:54:05 jan Exp $
+ * $Horde: chora/history.php,v 1.53.8.6 2009/01/06 15:22:34 jan Exp $
  *
- * Copyright 2000-2007 Anil Madhavapeddy <anil at recoil.org>
+ * Copyright 2000-2009 The Horde Project (http://www.horde.org/)
  *
- * See the enclosed file COPYING for license information (GPL).  If you
+ * 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  Anil Madhavapeddy <avsm at horde.org>
  */
 
 @define('CHORA_BASE', dirname(__FILE__));
 require_once CHORA_BASE . '/lib/base.php';
 
-// Exit if it's not supported.
+/* Exit if it's not supported. */
 if (is_a($VC, 'VC_svn')) {
     header('Location: ' . Chora::url('browse', $where));
     exit;
 }
 
-/* Spawn the file object */
+/* Spawn the file object. */
 $fl = &$VC->getFileObject($where, $cache);
 Chora::checkError($fl);
 
-/* $trunk contains an array of trunk revisions */
+/* $trunk contains an array of trunk revisions. */
 $trunk = array();
 
-/* $branches is a hash with the branch revision as the
- * key, and value being an array of revs on that branch */
+/* $branches is a hash with the branch revision as the key, and value
+ * being an array of revs on that branch. */
 $branches = array();
 
-/* Populate $col with a list of all the branch points */
+/* Populate $col with a list of all the branch points. */
 foreach ($fl->branches as $rev => $sym) {
     $branches[$rev] = array();
 }
 
-/* For every revision, figure out if it is a trunk
- * revision, or instead associated with a branch.
- * If trunk, add it to the $trunk array.
- * Otherwise, add it to an array in $branches[$branch]
- */
-
+/* For every revision, figure out if it is a trunk revision, or
+ * instead associated with a branch.  If trunk, add it to the $trunk
+ * array.  Otherwise, add it to an array in $branches[$branch]. */
 foreach ($fl->logs as $log) {
     $rev = $log->queryRevision();
     $baseRev = VC_Revision::strip($rev, 1);
@@ -49,106 +48,110 @@ foreach ($fl->logs as $log) {
             $branchFound = true;
         }
     }
-    /* If its not a branch, then add it to the trunk */
-    /* TODO: this silently drops vendor branches atm! - avsm */
+    /* If its not a branch, then add it to the trunk. */
+    /* TODO: this silently drops vendor branches atm! - avsm. */
     if (!$branchFound && VC_Revision::sizeof($rev) == 2) {
         array_unshift($trunk, $rev);
     }
 }
 
 foreach ($branches as $col => $rows) {
-    /* If this branch has no actual commits on it, then it's a
-     * stub branch, and we can remove it for this view */
+    /* If this branch has no actual commits on it, then it's a stub
+     * branch, and we can remove it for this view. */
     if (!sizeof($rows)) {
         unset($branches[$col]);
     }
 }
 
-$colset = array('#ccdeff','#eeccff','#ffeecc','#eeffcc','#ccffdd','#dcdba0');
+$colset = array('#ccdeff', '#ecf', '#fec', '#efc', '#cfd', '#dcdba0');
 $colStack = array();
 $branchColours = array();
 foreach ($branches as $brrev => $brcont) {
-   if (!sizeof($colStack)) $colStack = $colset;
-   $branchColours[$brrev] = array_shift($colset);
+    if (!count($colStack)) {
+        $colStack = $colset;
+    }
+    $branchColours[$brrev] = array_shift($colset);
 }
 
-/* This takes a row and a column, and recursively iterates through
- * any sub-revisions or branches from the value that was already in
- * the grid at the co-ordinates that it was called with.
+/**
+ * This takes a row and a column, and recursively iterates through any
+ * sub-revisions or branches from the value that was already in the
+ * grid at the co-ordinates that it was called with.
  *
- * Calling this function on every revision of the trunk is enough
- * to render out the whole tree. */
-
-function populateGrid($row, $col) {
+ * Calling this function on every revision of the trunk is enough to
+ * render out the whole tree.
+ */
+function populateGrid($row, $col)
+{
     global $grid, $branches;
 
-    /* Figure out the starting revision this function uses */
+    /* Figure out the starting revision this function uses. */
     $rev = $grid[$row][$col];
 
-    /* For every branch that is known, try to see if it forks here */
+    /* For every branch that is known, try to see if it forks here. */
     $brkeys = array_keys($branches);
 
     /* NOTE: do not optimise to use foreach () or each() here, as that
      * really screws up the $branches pointer array due to the
-     * recursion, and parallel branches fail - avsm */
-    for ($a = 0; $a < sizeof($brkeys); $a++) {
+     * recursion, and parallel branches fail - avsm. */
+    for ($a = 0, $aMax = count($brkeys); $a < $aMax; ++$a) {
         $brrev = $brkeys[$a];
         $brcont = $branches[$brrev];
-        /* Check to see if current point matches a branch point */
+        /* Check to see if current point matches a branch point. */
         if (!strcmp($rev, VC_Revision::strip($brrev, 1))) {
-            /* If it does, figure out how many rows we have to add */
+            /* If it does, figure out how many rows we have to add. */
             $numRows = sizeof($brcont);
-            /* Check rows in columns to the right, until one is free */
+            /* Check rows in columns to the right, until one is
+             * free. */
             $insCol = $col + 1;
             while (true) {
-                /* Look in the current column for a set value */
+                /* Look in the current column for a set value. */
                 $inc = false;
-                for ($i = $row; $i <= ($row + $numRows); $i++) {
+                for ($i = $row; $i <= ($row + $numRows); ++$i) {
                     if (isset($grid[$i][$insCol])) {
                         $inc = true;
                     }
                 }
                 /* If a set value was found, shift to the right and
-                 * try again.  Otherwise, break out of the loop */
+                 * try again.  Otherwise, break out of the loop. */
                 if ($inc) {
                     if (!isset($grid[$row][$insCol])) {
                         $grid[$row][$insCol] = ':' . $brcont[0];
                     }
-                    $insCol++;
+                    ++$insCol;
                 } else {
                     break;
                 }
             }
 
-            /* Put a fork marker in the top of the branch */
+            /* Put a fork marker in the top of the branch. */
             $grid[$row][$insCol] = $brrev;
 
-            /* Populate the grid with the branch values at this point */
-            for ($i = 0; $i < $numRows; $i++) {
+            /* Populate the grid with the branch values at this
+             * point. */
+            for ($i = 0; $i < $numRows; ++$i) {
                 $grid[1 + $i + $row][$insCol] = $brcont[$i];
             }
-            /* For each value just set, check for sub-branches,
-             * - but in reverse (VERY IMPORTANT!) */
-            for ($i = $numRows - 1; $i >= 0 ; $i--) {
+            /* For each value just set, check for sub-branches, - but
+             * in reverse (VERY IMPORTANT!). */
+            for ($i = $numRows - 1; $i >= 0 ; --$i) {
                 populateGrid(1+$i+$row, $insCol);
             }
         }
     }
 }
 
-/* Start row at the bottom trunk revision.  Since branches always
- * go down, there can never be one above 1.1, and so this is a
- * safe location to start.  We will then work our way up,
- * recursively populating the grid with branch revisions */
-
+/* Start row at the bottom trunk revision.  Since branches always go
+ * down, there can never be one above 1.1, and so this is a safe
+ * location to start.  We will then work our way up, recursively
+ * populating the grid with branch revisions. */
 for ($row = sizeof($trunk) - 1; $row >= 0; $row--) {
     $grid[$row][0] = $trunk[$row];
     populateGrid($row, 0);
 }
 
-/* Sort the grid array into row order, and determine the maximum column
- * size that we need to render out in HTML */
-
+/* Sort the grid array into row order, and determine the maximum
+ * column size that we need to render out in HTML. */
 ksort($grid);
 $maxCol = 0;
 foreach ($grid as $cols) {
@@ -166,37 +169,38 @@ require CHORA_TEMPLATES . '/headerbar.inc';
 require CHORA_TEMPLATES . '/history/header.inc';
 
 foreach ($grid as $row) {
-    require CHORA_TEMPLATES . '/history/row_start.inc';
+    echo '<tr>';
 
-    /* Start traversing the grid of rows and columns */
-    for ($i = 0; $i <= $maxCol; $i++) {
+    /* Start traversing the grid of rows and columns. */
+    for ($i = 0; $i <= $maxCol; ++$i) {
 
-        /* If this column has nothing in it, require a blank cell */
+        /* If this column has nothing in it, require a blank cell. */
         if (!isset($row[$i])) {
              $bg = '';
-             require CHORA_TEMPLATES.'/history/blank.inc';
+             require CHORA_TEMPLATES . '/history/blank.inc';
              continue;
         }
 
-        /* Otherwise, this cell has content; determine what it is */
+        /* Otherwise, this cell has content; determine what it is. */
         $rev = $row[$i];
 
         if (VC_Revision::valid($rev) && (VC_Revision::sizeof($rev) % 2)) {
-            /* This is a branch point, so put the info out */
-            $bg = isset($branchColours[$rev]) ? $branchColours[$rev] : 'white';
+            /* This is a branch point, so put the info out. */
+            $bg = isset($branchColours[$rev]) ? $branchColours[$rev] : '#e9e9e9';
             $symname = $fl->branches[$rev];
             require CHORA_TEMPLATES . '/history/branch_cell.inc';
 
         } elseif (preg_match('|^:|', $rev)) {
-            /* This is a continuation cell, so render it with the branch colour */
+            /* This is a continuation cell, so render it with the
+             * branch colour. */
             $bgbr = VC_Revision::strip(preg_replace('|^\:|', '', $rev), 1);
-            $bg = isset($branchColours[$bgbr]) ? $branchColours[$bgbr] : 'white';
+            $bg = isset($branchColours[$bgbr]) ? $branchColours[$bgbr] : '#e9e9e9';
             require CHORA_TEMPLATES . '/history/blank.inc';
 
         } elseif (VC_Revision::valid($rev)) {
-            /* This cell contains a revision, so render it */
+            /* This cell contains a revision, so render it. */
             $bgbr = VC_Revision::strip($rev, 1);
-            $bg = isset($branchColours[$bgbr]) ? $branchColours[$bgbr] : 'white';
+            $bg = isset($branchColours[$bgbr]) ? $branchColours[$bgbr] : '#e9e9e9';
             $log = $fl->logs[$rev];
             $author = Chora::showAuthorName($log->queryAuthor());
             $date = strftime('%e %b %Y', $log->queryDate());
@@ -204,11 +208,12 @@ foreach ($grid as $row) {
             require CHORA_TEMPLATES . '/history/rev.inc';
 
         } else {
-            /* Exhausted other possibilities, just show a blank cell */
+            /* Exhausted other possibilities, just show a blank cell. */
             require CHORA_TEMPLATES . '/history/blank.inc';
         }
     }
-    require CHORA_TEMPLATES . '/history/row_end.inc';
+
+    echo '</tr>';
 }
 
 require CHORA_TEMPLATES . '/history/footer.inc';
diff --git a/index.php b/index.php
index dfd82f4..9649bba 100644
--- a/index.php
+++ b/index.php
@@ -1,18 +1,20 @@
 <?php
 /**
- * $Horde: chora/index.php,v 1.25.10.3 2007/01/02 13:54:05 jan Exp $
+ * $Horde: chora/index.php,v 1.25.10.5 2009/01/06 15:22:34 jan Exp $
  *
- * Copyright 1999-2007 Anil Madhavapeddy <anil at recoil.org>
+ * Copyright 1999-2009 The Horde Project (http://www.horde.org/)
  *
- * See the enclosed file COPYING for license information (GPL).  If you
+ * 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  Anil Madhavapeddy <avsm at horde.org>
  */
 
 define('CHORA_BASE', dirname(__FILE__));
-$chora_configured = (@is_readable(CHORA_BASE . '/config/conf.php') &&
-                     @is_readable(CHORA_BASE . '/config/sourceroots.php') &&
-                     @is_readable(CHORA_BASE . '/config/mime_drivers.php') &&
-                     @is_readable(CHORA_BASE . '/config/prefs.php'));
+$chora_configured = (is_readable(CHORA_BASE . '/config/conf.php') &&
+                     is_readable(CHORA_BASE . '/config/sourceroots.php') &&
+                     is_readable(CHORA_BASE . '/config/mime_drivers.php') &&
+                     is_readable(CHORA_BASE . '/config/prefs.php'));
 
 if (!$chora_configured) {
     /* Chora isn't configured. */
diff --git a/js/QuickFinder.js b/js/QuickFinder.js
new file mode 100644
index 0000000..2d23ade
--- /dev/null
+++ b/js/QuickFinder.js
@@ -0,0 +1 @@
+var QuickFinder={attachBehavior:function(){$$("input").each(function(C){var B=C.readAttribute("for");if(!B){return }if(B.indexOf(",")!=-1){C.filterTargets=[];var A=B.split(",");for(var F=0;F<A.length;++F){var E=$(A[F]);if(E){C.filterTargets.push(E)}}if(!C.filterTargets.length){return }}else{C.filterTargets=[$(B)];if(!C.filterTargets[0]){return }}filterEmpty=C.readAttribute("empty");if(filterEmpty){C.filterEmpty=$(filterEmpty)}C.observe("keyup",QuickFinder.onKeyUp);for(var F=0,D=C.filterTargets.length;F<D;F++){C.filterTargets[F].immediateDescendants().each(function(G){var H=G.filterText||G.readAttribute("filterText");if(!H){G.filterText=G.innerHTML.stripTags()}G.filterText=G.filterText.toLowerCase()})}QuickFinder.filter(C)})},onKeyUp:function(A){input=A.element();if(input.filterTargets){QuickFinder.filter(input)}},filter:function(B){var F=B.value.toLowerCase();var A=0;for(var D=0,C=B.filterTargets.length;D<C;D++){B.filterTargets[D].immediateDescendants().each(function(G){var H=G.filterText;if(H.indexOf(F)==-1){G.addClassName("QuickFinderNoMatch")}else{++A;G.removeClassName("QuickFinderNoMatch")}})}try{if(B.filterEmpty){(A==0)?B.filterEmpty.show():B.filterEmpty.hide()}}catch(E){}}};document.observe("dom:loaded",QuickFinder.attachBehavior)
\ No newline at end of file
diff --git a/js/prototype.js b/js/prototype.js
new file mode 100644
index 0000000..3c2493f
--- /dev/null
+++ b/js/prototype.js
@@ -0,0 +1 @@
+var Prototype={Version:"1.6.0.3",Browser:{IE:!!(window.attachEvent&&navigator.userAgent.indexOf("Opera")===-1),Opera:navigator.userAgent.indexOf("Opera")>-1,WebKit:navigator.userAgent.indexOf("AppleWebKit/")>-1,Gecko:navigator.userAgent.indexOf("Gecko")>-1&&navigator.userAgent.indexOf("KHTML")===-1,MobileSafari:!!navigator.userAgent.match(/Apple.*Mobile.*Safari/)},BrowserFeatures:{XPath:!!document.evaluate,SelectorsAPI:!!document.querySelector,ElementExtensions:!!window.HTMLElement,SpecificElementExtensions:document.createElement("div")["__proto__"]&&document.createElement("div")["__proto__"]!==document.createElement("form")["__proto__"]},ScriptFragment:"<script[^>]*>([\\S\\s]*?)<\/script>",JSONFilter:/^\/\*-secure-([\s\S]*)\*\/\s*$/,emptyFunction:function(){},K:function(a){return a}};if(Prototype.Browser.MobileSafari){Prototype.BrowserFeatures.SpecificElementExtensions=false}var Class={create:function(){var e=null,d=$A(arguments);if(Object.isFunction(d[0])){e=d.shift()}function a(){this.initialize.apply(this,arguments)}Object.extend(a,Class.Methods);a.superclass=e;a.subclasses=[];if(e){var b=function(){};b.prototype=e.prototype;a.prototype=new b;e.subclasses.push(a)}for(var c=0;c<d.length;c++){a.addMethods(d[c])}if(!a.prototype.initialize){a.prototype.initialize=Prototype.emptyFunction}a.prototype.constructor=a;return a}};Class.Methods={addMethods:function(g){var c=this.superclass&&this.superclass.prototype;var b=Object.keys(g);if(!Object.keys({toString:true}).length){b.push("toString","valueOf")}for(var a=0,d=b.length;a<d;a++){var f=b[a],e=g[f];if(c&&Object.isFunction(e)&&e.argumentNames().first()=="$super"){var h=e;e=(function(i){return function(){return c[i].apply(this,arguments)}})(f).wrap(h);e.valueOf=h.valueOf.bind(h);e.toString=h.toString.bind(h)}this.prototype[f]=e}return this}};var Abstract={};Object.extend=function(a,c){for(var b in c){a[b]=c[b]}return a};Object.extend(Object,{inspect:function(a){try{if(Object.isUndefined(a)){return"undefined"}if(a===null){return"null"}return a.inspect?a.inspect():String(a)}catch(b){if(b instanceof RangeError){return"..."}throw b}},toJSON:function(a){var c=typeof a;switch(c){case"undefined":case"function":case"unknown":return;case"boolean":return a.toString()}if(a===null){return"null"}if(a.toJSON){return a.toJSON()}if(Object.isElement(a)){return}var b=[];for(var e in a){var d=Object.toJSON(a[e]);if(!Object.isUndefined(d)){b.push(e.toJSON()+": "+d)}}return"{"+b.join(", ")+"}"},toQueryString:function(a){return $H(a).toQueryString()},toHTML:function(a){return a&&a.toHTML?a.toHTML():String.interpret(a)},keys:function(a){var b=[];for(var c in a){b.push(c)}return b},values:function(b){var a=[];for(var c in b){a.push(b[c])}return a},clone:function(a){return Object.extend({},a)},isElement:function(a){return!!(a&&a.nodeType==1)},isArray:function(a){return a!=null&&typeof a=="object"&&"splice"in a&&"join"in a},isHash:function(a){return a instanceof Hash},isFunction:function(a){return typeof a=="function"},isString:function(a){return typeof a=="string"},isNumber:function(a){return typeof a=="number"},isUndefined:function(a){return typeof a=="undefined"}});Object.extend(Function.prototype,{argumentNames:function(){var a=this.toString().match(/^[\s\(]*function[^(]*\(([^\)]*)\)/)[1].replace(/\s+/g,"").split(",");return a.length==1&&!a[0]?[]:a},bind:function(){if(arguments.length<2&&Object.isUndefined(arguments[0])){return this}var a=this,c=$A(arguments),b=c.shift();return function(){return a.apply(b,c.concat($A(arguments)))}},bindAsEventListener:function(){var a=this,c=$A(arguments),b=c.shift();return function(d){return a.apply(b,[d||window.event].concat(c))}},curry:function(){if(!arguments.length){return this}var a=this,b=$A(arguments);return function(){return a.apply(this,b.concat($A(arguments)))}},delay:function(){var a=this,b=$A(arguments),c=b.shift()*1000;return window.setTimeout(function(){return a.apply(a,b)},c)},defer:function(){var a=[0.01].concat($A(arguments));return this.delay.apply(this,a)},wrap:function(b){var a=this;return function(){return b.apply(this,[a.bind(this)].concat($A(arguments)))}},methodize:function(){if(this._methodized){return this._methodized}var a=this;return this._methodized=function(){return a.apply(null,[this].concat($A(arguments)))}}});Date.prototype.toJSON=function(){return'"'+this.getUTCFullYear()+"-"+(this.getUTCMonth()+1).toPaddedString(2)+"-"+this.getUTCDate().toPaddedString(2)+"T"+this.getUTCHours().toPaddedString(2)+":"+this.getUTCMinutes().toPaddedString(2)+":"+this.getUTCSeconds().toPaddedString(2)+'Z"'};var Try={these:function(){var c;for(var b=0,d=arguments.length;b<d;b++){var a=arguments[b];try{c=a();break}catch(f){}}return c}};RegExp.prototype.match=RegExp.prototype.test;RegExp.escape=function(a){return String(a).replace(/([.*+?^=!:${}()|[\]\/\\])/g,"\\$1")};var PeriodicalExecuter=Class.create({initialize:function(b,a){this.callback=b;this.frequency=a;this.currentlyExecuting=false;this.registerCallback()},registerCallback:function(){this.timer=setInterval(this.onTimerEvent.bind(this),this.frequency*1000)},execute:function(){this.callback(this)},stop:function(){if(!this.timer){return}clearInterval(this.timer);this.timer=null},onTimerEvent:function(){if(!this.currentlyExecuting){try{this.currentlyExecuting=true;this.execute()}finally{this.currentlyExecuting=false}}}});Object.extend(String,{interpret:function(a){return a==null?"":String(a)},specialChar:{"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r","\\":"\\\\"}});Object.extend(String.prototype,{gsub:function(e,c){var a="",d=this,b;c=arguments.callee.prepareReplacement(c);while(d.length>0){if(b=d.match(e)){a+=d.slice(0,b.index);a+=String.interpret(c(b));d=d.slice(b.index+b[0].length)}else{a+=d,d=""}}return a},sub:function(c,a,b){a=this.gsub.prepareReplacement(a);b=Object.isUndefined(b)?1:b;return this.gsub(c,function(d){if(--b<0){return d[0]}return a(d)})},scan:function(b,a){this.gsub(b,a);return String(this)},truncate:function(b,a){b=b||30;a=Object.isUndefined(a)?"...":a;return this.length>b?this.slice(0,b-a.length)+a:String(this)},strip:function(){return this.replace(/^\s+/,"").replace(/\s+$/,"")},stripTags:function(){return this.replace(/<\/?[^>]+>/gi,"")},stripScripts:function(){return this.replace(new RegExp(Prototype.ScriptFragment,"img"),"")},extractScripts:function(){var b=new RegExp(Prototype.ScriptFragment,"img");var a=new RegExp(Prototype.ScriptFragment,"im");return(this.match(b)||[]).map(function(c){return(c.match(a)||["",""])[1]})},evalScripts:function(){return this.extractScripts().map(function(script){return eval(script)})},escapeHTML:function(){var a=arguments.callee;a.text.data=this;return a.div.innerHTML},unescapeHTML:function(){var a=new Element("div");a.innerHTML=this.stripTags();return a.childNodes[0]?(a.childNodes.length>1?$A(a.childNodes).inject("",function(b,c){return b+c.nodeValue}):a.childNodes[0].nodeValue):""},toQueryParams:function(b){var a=this.strip().match(/([^?#]*)(#.*)?$/);if(!a){return{}}return a[1].split(b||"&").inject({},function(e,f){if((f=f.split("="))[0]){var c=decodeURIComponent(f.shift());var d=f.length>1?f.join("="):f[0];if(d!=undefined){d=decodeURIComponent(d)}if(c in e){if(!Object.isArray(e[c])){e[c]=[e[c]]}e[c].push(d)}else{e[c]=d}}return e})},toArray:function(){return this.split("")},succ:function(){return this.slice(0,this.length-1)+String.fromCharCode(this.charCodeAt(this.length-1)+1)},times:function(a){return a<1?"":new Array(a+1).join(this)},camelize:function(){var d=this.split("-"),a=d.length;if(a==1){return d[0]}var c=this.charAt(0)=="-"?d[0].charAt(0).toUpperCase()+d[0].substring(1):d[0];for(var b=1;b<a;b++){c+=d[b].charAt(0).toUpperCase()+d[b].substring(1)}return c},capitalize:function(){return this.charAt(0).toUpperCase()+this.substring(1).toLowerCase()},underscore:function(){return this.gsub(/::/,"/").gsub(/([A-Z]+)([A-Z][a-z])/,"#{1}_#{2}").gsub(/([a-z\d])([A-Z])/,"#{1}_#{2}").gsub(/-/,"_").toLowerCase()},dasherize:function(){return this.gsub(/_/,"-")},inspect:function(b){var a=this.gsub(/[\x00-\x1f\\]/,function(c){var d=String.specialChar[c[0]];return d?d:"\\u00"+c[0].charCodeAt().toPaddedString(2,16)});if(b){return'"'+a.replace(/"/g,'\\"')+'"'}return"'"+a.replace(/'/g,"\\'")+"'"},toJSON:function(){return this.inspect(true)},unfilterJSON:function(a){return this.sub(a||Prototype.JSONFilter,"#{1}")},isJSON:function(){var a=this;if(a.blank()){return false}a=this.replace(/\\./g,"@").replace(/"[^"\\\n\r]*"/g,"");return(/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(a)},evalJSON:function(sanitize){var json=this.unfilterJSON();try{if(!sanitize||json.isJSON()){return eval("("+json+")")}}catch(e){}throw new SyntaxError("Badly formed JSON string: "+this.inspect())},include:function(a){return this.indexOf(a)>-1},startsWith:function(a){return this.indexOf(a)===0},endsWith:function(a){var b=this.length-a.length;return b>=0&&this.lastIndexOf(a)===b},empty:function(){return this==""},blank:function(){return/^\s*$/.test(this)},interpolate:function(a,b){return new Template(this,b).evaluate(a)}});if(Prototype.Browser.WebKit||Prototype.Browser.IE){Object.extend(String.prototype,{escapeHTML:function(){return this.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")},unescapeHTML:function(){return this.stripTags().replace(/&amp;/g,"&").replace(/&lt;/g,"<").replace(/&gt;/g,">")}})}String.prototype.gsub.prepareReplacement=function(b){if(Object.isFunction(b)){return b}var a=new Template(b);return function(c){return a.evaluate(c)}};String.prototype.parseQuery=String.prototype.toQueryParams;Object.extend(String.prototype.escapeHTML,{div:document.createElement("div"),text:document.createTextNode("")});String.prototype.escapeHTML.div.appendChild(String.prototype.escapeHTML.text);var Template=Class.create({initialize:function(a,b){this.template=a.toString();this.pattern=b||Template.Pattern},evaluate:function(a){if(Object.isFunction(a.toTemplateReplacements)){a=a.toTemplateReplacements()}return this.template.gsub(this.pattern,function(d){if(a==null){return""}var f=d[1]||"";if(f=="\\"){return d[2]}var b=a,g=d[3];var e=/^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;d=e.exec(g);if(d==null){return f}while(d!=null){var c=d[1].startsWith("[")?d[2].gsub("\\\\]","]"):d[1];b=b[c];if(null==b||""==d[3]){break}g=g.substring("["==d[3]?d[1].length:d[0].length);d=e.exec(g)}return f+String.interpret(b)})}});Template.Pattern=/(^|.|\r|\n)(#\{(.*?)\})/;var $break={};var Enumerable={each:function(c,b){var a=0;try{this._each(function(e){c.call(b,e,a++)})}catch(d){if(d!=$break){throw d}}return this},eachSlice:function(d,c,b){var a=-d,e=[],f=this.toArray();if(d<1){return f}while((a+=d)<f.length){e.push(f.slice(a,a+d))}return e.collect(c,b)},all:function(c,b){c=c||Prototype.K;var a=true;this.each(function(e,d){a=a&&!!c.call(b,e,d);if(!a){throw $break}});return a},any:function(c,b){c=c||Prototype.K;var a=false;this.each(function(e,d){if(a=!!c.call(b,e,d)){throw $break}});return a},collect:function(c,b){c=c||Prototype.K;var a=[];this.each(function(e,d){a.push(c.call(b,e,d))});return a},detect:function(c,b){var a;this.each(function(e,d){if(c.call(b,e,d)){a=e;throw $break}});return a},findAll:function(c,b){var a=[];this.each(function(e,d){if(c.call(b,e,d)){a.push(e)}});return a},grep:function(d,c,b){c=c||Prototype.K;var a=[];if(Object.isString(d)){d=new RegExp(d)}this.each(function(f,e){if(d.match(f)){a.push(c.call(b,f,e))}});return a},include:function(a){if(Object.isFunction(this.indexOf)){if(this.indexOf(a)!=-1){return true}}var b=false;this.each(function(c){if(c==a){b=true;throw $break}});return b},inGroupsOf:function(b,a){a=Object.isUndefined(a)?null:a;return this.eachSlice(b,function(c){while(c.length<b){c.push(a)}return c})},inject:function(a,c,b){this.each(function(e,d){a=c.call(b,a,e,d)});return a},invoke:function(b){var a=$A(arguments).slice(1);return this.map(function(c){return c[b].apply(c,a)})},max:function(c,b){c=c||Prototype.K;var a;this.each(function(e,d){e=c.call(b,e,d);if(a==null||e>=a){a=e}});return a},min:function(c,b){c=c||Prototype.K;var a;this.each(function(e,d){e=c.call(b,e,d);if(a==null||e<a){a=e}});return a},partition:function(d,b){d=d||Prototype.K;var c=[],a=[];this.each(function(f,e){(d.call(b,f,e)?c:a).push(f)});return[c,a]},pluck:function(b){var a=[];this.each(function(c){a.push(c[b])});return a},reject:function(c,b){var a=[];this.each(function(e,d){if(!c.call(b,e,d)){a.push(e)}});return a},sortBy:function(b,a){return this.map(function(d,c){return{value:d,criteria:b.call(a,d,c)}}).sort(function(f,e){var d=f.criteria,c=e.criteria;return d<c?-1:d>c?1:0}).pluck("value")},toArray:function(){return this.map()},zip:function(){var b=Prototype.K,a=$A(arguments);if(Object.isFunction(a.last())){b=a.pop()}var c=[this].concat(a).map($A);return this.map(function(e,d){return b(c.pluck(d))})},size:function(){return this.toArray().length},inspect:function(){return"#<Enumerable:"+this.toArray().inspect()+">"}};Object.extend(Enumerable,{map:Enumerable.collect,find:Enumerable.detect,select:Enumerable.findAll,filter:Enumerable.findAll,member:Enumerable.include,entries:Enumerable.toArray,every:Enumerable.all,some:Enumerable.any});function $A(c){if(!c){return[]}if(c.toArray){return c.toArray()}var b=c.length||0,a=new Array(b);while(b--){a[b]=c[b]}return a}if(Prototype.Browser.WebKit){$A=function(c){if(!c){return[]}if(!(typeof c==="function"&&typeof c.length==="number"&&typeof c.item==="function")&&c.toArray){return c.toArray()}var b=c.length||0,a=new Array(b);while(b--){a[b]=c[b]}return a}}Array.from=$A;Object.extend(Array.prototype,Enumerable);if(!Array.prototype._reverse){Array.prototype._reverse=Array.prototype.reverse}Object.extend(Array.prototype,{_each:function(b){for(var a=0,c=this.length;a<c;a++){b(this[a])}},clear:function(){this.length=0;return this},first:function(){return this[0]},last:function(){return this[this.length-1]},compact:function(){return this.select(function(a){return a!=null})},flatten:function(){return this.inject([],function(b,a){return b.concat(Object.isArray(a)?a.flatten():[a])})},without:function(){var a=$A(arguments);return this.select(function(b){return!a.include(b)})},reverse:function(a){return(a!==false?this:this.toArray())._reverse()},reduce:function(){return this.length>1?this:this[0]},uniq:function(a){return this.inject([],function(d,c,b){if(0==b||(a?d.last()!=c:!d.include(c))){d.push(c)}return d})},intersect:function(a){return this.uniq().findAll(function(b){return a.detect(function(c){return b===c})})},clone:function(){return[].concat(this)},size:function(){return this.length},inspect:function(){return"["+this.map(Object.inspect).join(", ")+"]"},toJSON:function(){var a=[];this.each(function(b){var c=Object.toJSON(b);if(!Object.isUndefined(c)){a.push(c)}});return"["+a.join(", ")+"]"}});if(Object.isFunction(Array.prototype.forEach)){Array.prototype._each=Array.prototype.forEach}if(!Array.prototype.indexOf){Array.prototype.indexOf=function(c,a){a||(a=0);var b=this.length;if(a<0){a=b+a}for(;a<b;a++){if(this[a]===c){return a}}return-1}}if(!Array.prototype.lastIndexOf){Array.prototype.lastIndexOf=function(b,a){a=isNaN(a)?this.length:(a<0?this.length+a:a)+1;var c=this.slice(0,a).reverse().indexOf(b);return(c<0)?c:a-c-1}}Array.prototype.toArray=Array.prototype.clone;function $w(a){if(!Object.isString(a)){return[]}a=a.strip();return a?a.split(/\s+/):[]}if(Prototype.Browser.Opera){Array.prototype.concat=function(){var e=[];for(var b=0,c=this.length;b<c;b++){e.push(this[b])}for(var b=0,c=arguments.length;b<c;b++){if(Object.isArray(arguments[b])){for(var a=0,d=arguments[b].length;a<d;a++){e.push(arguments[b][a])}}else{e.push(arguments[b])}}return e}}Object.extend(Number.prototype,{toColorPart:function(){return this.toPaddedString(2,16)},succ:function(){return this+1},times:function(b,a){$R(0,this,true).each(b,a);return this},toPaddedString:function(c,b){var a=this.toString(b||10);return"0".times(c-a.length)+a},toJSON:function(){return isFinite(this)?this.toString():"null"}});$w("abs round ceil floor").each(function(a){Number.prototype[a]=Math[a].methodize()});function $H(a){return new Hash(a)}var Hash=Class.create(Enumerable,(function(){function a(b,c){if(Object.isUndefined(c)){return b}return b+"="+encodeURIComponent(String.interpret(c))}return{initialize:function(b){this._object=Object.isHash(b)?b.toObject():Object.clone(b)},_each:function(c){for(var b in this._object){var d=this._object[b],e=[b,d];e.key=b;e.value=d;c(e)}},set:function(b,c){return this._object[b]=c},get:function(b){if(this._object[b]!==Object.prototype[b]){return this._object[b]}},unset:function(b){var c=this._object[b];delete this._object[b];return c},toObject:function(){return Object.clone(this._object)},keys:function(){return this.pluck("key")},values:function(){return this.pluck("value")},index:function(c){var b=this.detect(function(d){return d.value===c});return b&&b.key},merge:function(b){return this.clone().update(b)},update:function(b){return new Hash(b).inject(this,function(c,d){c.set(d.key,d.value);return c})},toQueryString:function(){return this.inject([],function(d,e){var c=encodeURIComponent(e.key),b=e.value;if(b&&typeof b=="object"){if(Object.isArray(b)){return d.concat(b.map(a.curry(c)))}}else{d.push(a(c,b))}return d}).join("&")},inspect:function(){return"#<Hash:{"+this.map(function(b){return b.map(Object.inspect).join(": ")}).join(", ")+"}>"},toJSON:function(){return Object.toJSON(this.toObject())},clone:function(){return new Hash(this)}}})());Hash.prototype.toTemplateReplacements=Hash.prototype.toObject;Hash.from=$H;var ObjectRange=Class.create(Enumerable,{initialize:function(c,a,b){this.start=c;this.end=a;this.exclusive=b},_each:function(a){var b=this.start;while(this.include(b)){a(b);b=b.succ()}},include:function(a){if(a<this.start){return false}if(this.exclusive){return a<this.end}return a<=this.end}});var $R=function(c,a,b){return new ObjectRange(c,a,b)};var Ajax={getTransport:function(){return Try.these(function(){return new XMLHttpRequest()},function(){return new ActiveXObject("Msxml2.XMLHTTP")},function(){return new ActiveXObject("Microsoft.XMLHTTP")})||false},activeRequestCount:0};Ajax.Responders={responders:[],_each:function(a){this.responders._each(a)},register:function(a){if(!this.include(a)){this.responders.push(a)}},unregister:function(a){this.responders=this.responders.without(a)},dispatch:function(d,b,c,a){this.each(function(f){if(Object.isFunction(f[d])){try{f[d].apply(f,[b,c,a])}catch(g){}}})}};Object.extend(Ajax.Responders,Enumerable);Ajax.Responders.register({onCreate:function(){Ajax.activeRequestCount++},onComplete:function(){Ajax.activeRequestCount--}});Ajax.Base=Class.create({initialize:function(a){this.options={method:"post",asynchronous:true,contentType:"application/x-www-form-urlencoded",encoding:"UTF-8",parameters:"",evalJSON:true,evalJS:true};Object.extend(this.options,a||{});this.options.method=this.options.method.toLowerCase();if(Object.isString(this.options.parameters)){this.options.parameters=this.options.parameters.toQueryParams()}else{if(Object.isHash(this.options.parameters)){this.options.parameters=this.options.parameters.toObject()}}}});Ajax.Request=Class.create(Ajax.Base,{_complete:false,initialize:function($super,b,a){$super(a);this.transport=Ajax.getTransport();this.request(b)},request:function(b){this.url=b;this.method=this.options.method;var d=Object.clone(this.options.parameters);if(!["get","post"].include(this.method)){d._method=this.method;this.method="post"}this.parameters=d;if(d=Object.toQueryString(d)){if(this.method=="get"){this.url+=(this.url.include("?")?"&":"?")+d}else{if(/Konqueror|Safari|KHTML/.test(navigator.userAgent)){d+="&_="}}}try{var a=new Ajax.Response(this);if(this.options.onCreate){this.options.onCreate(a)}Ajax.Responders.dispatch("onCreate",this,a);this.transport.open(this.method.toUpperCase(),this.url,this.options.asynchronous);if(this.options.asynchronous){this.respondToReadyState.bind(this).defer(1)}this.transport.onreadystatechange=this.onStateChange.bind(this);this.setRequestHeaders();this.body=this.method=="post"?(this.options.postBody||d):null;this.transport.send(this.body);if(!this.options.asynchronous&&this.transport.overrideMimeType){this.onStateChange()}}catch(c){this.dispatchException(c)}},onStateChange:function(){var a=this.transport.readyState;if(a>1&&!((a==4)&&this._complete)){this.respondToReadyState(this.transport.readyState)}},setRequestHeaders:function(){var e={"X-Requested-With":"XMLHttpRequest","X-Prototype-Version":Prototype.Version,Accept:"text/javascript, text/html, application/xml, text/xml, */*"};if(this.method=="post"){e["Content-type"]=this.options.contentType+(this.options.encoding?"; charset="+this.options.encoding:"");if(this.transport.overrideMimeType&&(navigator.userAgent.match(/Gecko\/(\d{4})/)||[0,2005])[1]<2005){e.Connection="close"}}if(typeof this.options.requestHeaders=="object"){var c=this.options.requestHeaders;if(Object.isFunction(c.push)){for(var b=0,d=c.length;b<d;b+=2){e[c[b]]=c[b+1]}}else{$H(c).each(function(f){e[f.key]=f.value})}}for(var a in e){this.transport.setRequestHeader(a,e[a])}},success:function(){var a=this.getStatus();return!a||(a>=200&&a<300)},getStatus:function(){try{return this.transport.status||0}catch(a){return 0}},respondToReadyState:function(a){var c=Ajax.Request.Events[a],b=new Ajax.Response(this);if(c=="Complete"){try{this._complete=true;(this.options["on"+b.status]||this.options["on"+(this.success()?"Success":"Failure")]||Prototype.emptyFunction)(b,b.headerJSON)}catch(d){this.dispatchException(d)}var f=b.getHeader("Content-type");if(this.options.evalJS=="force"||(this.options.evalJS&&this.isSameOrigin()&&f&&f.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i))){this.evalResponse()}}try{(this.options["on"+c]||Prototype.emptyFunction)(b,b.headerJSON);Ajax.Responders.dispatch("on"+c,this,b,b.headerJSON)}catch(d){this.dispatchException(d)}if(c=="Complete"){this.transport.onreadystatechange=Prototype.emptyFunction}},isSameOrigin:function(){var a=this.url.match(/^\s*https?:\/\/[^\/]*/);return!a||(a[0]=="#{protocol}//#{domain}#{port}".interpolate({protocol:location.protocol,domain:document.domain,port:location.port?":"+location.port:""}))},getHeader:function(a){try{return this.transport.getResponseHeader(a)||null}catch(b){return null}},evalResponse:function(){try{return eval((this.transport.responseText||"").unfilterJSON())}catch(e){this.dispatchException(e)}},dispatchException:function(a){(this.options.onException||Prototype.emptyFunction)(this,a);Ajax.Responders.dispatch("onException",this,a)}});Ajax.Request.Events=["Uninitialized","Loading","Loaded","Interactive","Complete"];Ajax.Response=Class.create({initialize:function(c){this.request=c;var d=this.transport=c.transport,a=this.readyState=d.readyState;if((a>2&&!Prototype.Browser.IE)||a==4){this.status=this.getStatus();this.statusText=this.getStatusText();this.responseText=String.interpret(d.responseText);this.headerJSON=this._getHeaderJSON()}if(a==4){var b=d.responseXML;this.responseXML=Object.isUndefined(b)?null:b;this.responseJSON=this._getResponseJSON()}},status:0,statusText:"",getStatus:Ajax.Request.prototype.getStatus,getStatusText:function(){try{return this.transport.statusText||""}catch(a){return""}},getHeader:Ajax.Request.prototype.getHeader,getAllHeaders:function(){try{return this.getAllResponseHeaders()}catch(a){return null}},getResponseHeader:function(a){return this.transport.getResponseHeader(a)},getAllResponseHeaders:function(){return this.transport.getAllResponseHeaders()},_getHeaderJSON:function(){var a=this.getHeader("X-JSON");if(!a){return null}a=decodeURIComponent(escape(a));try{return a.evalJSON(this.request.options.sanitizeJSON||!this.request.isSameOrigin())}catch(b){this.request.dispatchException(b)}},_getResponseJSON:function(){var a=this.request.options;if(!a.evalJSON||(a.evalJSON!="force"&&!(this.getHeader("Content-type")||"").include("application/json"))||this.responseText.blank()){return null}try{return this.responseText.evalJSON(a.sanitizeJSON||!this.request.isSameOrigin())}catch(b){this.request.dispatchException(b)}}});Ajax.Updater=Class.create(Ajax.Request,{initialize:function($super,a,c,b){this.container={success:(a.success||a),failure:(a.failure||(a.success?null:a))};b=Object.clone(b);var d=b.onComplete;b.onComplete=(function(e,f){this.updateContent(e.responseText);if(Object.isFunction(d)){d(e,f)}}).bind(this);$super(c,b)},updateContent:function(d){var c=this.container[this.success()?"success":"failure"],a=this.options;if(!a.evalScripts){d=d.stripScripts()}if(c=$(c)){if(a.insertion){if(Object.isString(a.insertion)){var b={};b[a.insertion]=d;c.insert(b)}else{a.insertion(c,d)}}else{c.update(d)}}}});Ajax.PeriodicalUpdater=Class.create(Ajax.Base,{initialize:function($super,a,c,b){$super(b);this.onComplete=this.options.onComplete;this.frequency=(this.options.frequency||2);this.decay=(this.options.decay||1);this.updater={};this.container=a;this.url=c;this.start()},start:function(){this.options.onComplete=this.updateComplete.bind(this);this.onTimerEvent()},stop:function(){this.updater.options.onComplete=undefined;clearTimeout(this.timer);(this.onComplete||Prototype.emptyFunction).apply(this,arguments)},updateComplete:function(a){if(this.options.decay){this.decay=(a.responseText==this.lastText?this.decay*this.options.decay:1);this.lastText=a.responseText}this.timer=this.onTimerEvent.bind(this).delay(this.decay*this.frequency)},onTimerEvent:function(){this.updater=new Ajax.Updater(this.container,this.url,this.options)}});function $(b){if(arguments.length>1){for(var a=0,d=[],c=arguments.length;a<c;a++){d.push($(arguments[a]))}return d}if(Object.isString(b)){b=document.getElementById(b)}return Element.extend(b)}if(Prototype.BrowserFeatures.XPath){document._getElementsByXPath=function(f,a){var c=[];var e=document.evaluate(f,$(a)||document,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);for(var b=0,d=e.snapshotLength;b<d;b++){c.push(Element.extend(e.snapshotItem(b)))}return c}}if(!window.Node){var Node={}}if(!Node.ELEMENT_NODE){Object.extend(Node,{ELEMENT_NODE:1,ATTRIBUTE_NODE:2,TEXT_NODE:3,CDATA_SECTION_NODE:4,ENTITY_REFERENCE_NODE:5,ENTITY_NODE:6,PROCESSING_INSTRUCTION_NODE:7,COMMENT_NODE:8,DOCUMENT_NODE:9,DOCUMENT_TYPE_NODE:10,DOCUMENT_FRAGMENT_NODE:11,NOTATION_NODE:12})}(function(){var a=this.Element;this.Element=function(d,c){c=c||{};d=d.toLowerCase();var b=Element.cache;if(Prototype.Browser.IE&&c.name){d="<"+d+' name="'+c.name+'">';delete c.name;return Element.writeAttribute(document.createElement(d),c)}if(!b[d]){b[d]=Element.extend(document.createElement(d))}return Element.writeAttribute(b[d].cloneNode(false),c)};Object.extend(this.Element,a||{});if(a){this.Element.prototype=a.prototype}}).call(window);Element.cache={};Element.Methods={visible:function(a){return $(a).style.display!="none"},toggle:function(a){a=$(a);Element[Element.visible(a)?"hide":"show"](a);return a},hide:function(a){a=$(a);a.style.display="none";return a},show:function(a){a=$(a);a.style.display="";return a},remove:function(a){a=$(a);a.parentNode.removeChild(a);return a},update:function(a,b){a=$(a);if(b&&b.toElement){b=b.toElement()}if(Object.isElement(b)){return a.update().insert(b)}b=Object.toHTML(b);a.innerHTML=b.stripScripts();b.evalScripts.bind(b).defer();return a},replace:function(b,c){b=$(b);if(c&&c.toElement){c=c.toElement()}else{if(!Object.isElement(c)){c=Object.toHTML(c);var a=b.ownerDocument.createRange();a.selectNode(b);c.evalScripts.bind(c).defer();c=a.createContextualFragment(c.stripScripts())}}b.parentNode.replaceChild(c,b);return b},insert:function(c,e){c=$(c);if(Object.isString(e)||Object.isNumber(e)||Object.isElement(e)||(e&&(e.toElement||e.toHTML))){e={bottom:e}}var d,f,b,g;for(var a in e){d=e[a];a=a.toLowerCase();f=Element._insertionTranslations[a];if(d&&d.toElement){d=d.toElement()}if(Object.isElement(d)){f(c,d);continue}d=Object.toHTML(d);b=((a=="before"||a=="after")?c.parentNode:c).tagName.toUpperCase();g=Element._getContentFromAnonymousElement(b,d.stripScripts());if(a=="top"||a=="after"){g.reverse()}g.each(f.curry(c));d.evalScripts.bind(d).defer()}return c},wrap:function(b,c,a){b=$(b);if(Object.isElement(c)){$(c).writeAttribute(a||{})}else{if(Object.isString(c)){c=new Element(c,a)}else{c=new Element("div",c)}}if(b.parentNode){b.parentNode.replaceChild(c,b)}c.appendChild(b);return c},inspect:function(b){b=$(b);var a="<"+b.tagName.toLowerCase();$H({id:"id",className:"class"}).each(function(f){var e=f.first(),c=f.last();var d=(b[e]||"").toString();if(d){a+=" "+c+"="+d.inspect(true)}});return a+">"},recursivelyCollect:function(a,c){a=$(a);var b=[];while(a=a[c]){if(a.nodeType==1){b.push(Element.extend(a))}}return b},ancestors:function(a){return $(a).recursivelyCollect("parentNode")},descendants:function(a){return $(a).select("*")},firstDescendant:function(a){a=$(a).firstChild;while(a&&a.nodeType!=1){a=a.nextSibling}return $(a)},immediateDescendants:function(a){if(!(a=$(a).firstChild)){return[]}while(a&&a.nodeType!=1){a=a.nextSibling}if(a){return[a].concat($(a).nextSiblings())}return[]},previousSiblings:function(a){return $(a).recursivelyCollect("previousSibling")},nextSiblings:function(a){return $(a).recursivelyCollect("nextSibling")},siblings:function(a){a=$(a);return a.previousSiblings().reverse().concat(a.nextSiblings())},match:function(b,a){if(Object.isString(a)){a=new Selector(a)}return a.match($(b))},up:function(b,d,a){b=$(b);if(arguments.length==1){return $(b.parentNode)}var c=b.ancestors();return Object.isNumber(d)?c[d]:Selector.findElement(c,d,a)},down:function(b,c,a){b=$(b);if(arguments.length==1){return b.firstDescendant()}return Object.isNumber(c)?b.descendants()[c]:Element.select(b,c)[a||0]},previous:function(b,d,a){b=$(b);if(arguments.length==1){return $(Selector.handlers.previousElementSibling(b))}var c=b.previousSiblings();return Object.isNumber(d)?c[d]:Selector.findElement(c,d,a)},next:function(c,d,b){c=$(c);if(arguments.length==1){return $(Selector.handlers.nextElementSibling(c))}var a=c.nextSiblings();return Object.isNumber(d)?a[d]:Selector.findElement(a,d,b)},select:function(){var a=$A(arguments),b=$(a.shift());return Selector.findChildElements(b,a)},adjacent:function(){var a=$A(arguments),b=$(a.shift());return Selector.findChildElements(b.parentNode,a).without(b)},identify:function(b){b=$(b);var c=b.readAttribute("id"),a=arguments.callee;if(c){return c}do{c="anonymous_element_"+a.counter++}while($(c));b.writeAttribute("id",c);return c},readAttribute:function(c,a){c=$(c);if(Prototype.Browser.IE){var b=Element._attributeTranslations.read;if(b.values[a]){return b.values[a](c,a)}if(b.names[a]){a=b.names[a]}if(a.include(":")){return(!c.attributes||!c.attributes[a])?null:c.attributes[a].value}}return c.getAttribute(a)},writeAttribute:function(e,c,f){e=$(e);var b={},d=Element._attributeTranslations.write;if(typeof c=="object"){b=c}else{b[c]=Object.isUndefined(f)?true:f}for(var a in b){c=d.names[a]||a;f=b[a];if(d.values[a]){c=d.values[a](e,f)}if(f===false||f===null){e.removeAttribute(c)}else{if(f===true){e.setAttribute(c,c)}else{e.setAttribute(c,f)}}}return e},getHeight:function(a){return $(a).getDimensions().height},getWidth:function(a){return $(a).getDimensions().width},classNames:function(a){return new Element.ClassNames(a)},hasClassName:function(a,b){if(!(a=$(a))){return}var c=a.className;return(c.length>0&&(c==b||new RegExp("(^|\\s)"+b+"(\\s|$)").test(c)))},addClassName:function(a,b){if(!(a=$(a))){return}if(!a.hasClassName(b)){a.className+=(a.className?" ":"")+b}return a},removeClassName:function(a,b){if(!(a=$(a))){return}a.className=a.className.replace(new RegExp("(^|\\s+)"+b+"(\\s+|$)")," ").strip();return a},toggleClassName:function(a,b){if(!(a=$(a))){return}return a[a.hasClassName(b)?"removeClassName":"addClassName"](b)},cleanWhitespace:function(b){b=$(b);var c=b.firstChild;while(c){var a=c.nextSibling;if(c.nodeType==3&&!/\S/.test(c.nodeValue)){b.removeChild(c)}c=a}return b},empty:function(a){return $(a).innerHTML.blank()},descendantOf:function(b,a){b=$(b),a=$(a);if(b.compareDocumentPosition){return(b.compareDocumentPosition(a)&8)===8}if(a.contains){return a.contains(b)&&a!==b}while(b=b.parentNode){if(b==a){return true}}return false},scrollTo:function(a){a=$(a);var b=a.cumulativeOffset();window.scrollTo(b[0],b[1]);return a},getStyle:function(b,c){b=$(b);c=c=="float"?"cssFloat":c.camelize();var d=b.style[c];if(!d||d=="auto"){var a=document.defaultView.getComputedStyle(b,null);d=a?a[c]:null}if(c=="opacity"){return d?parseFloat(d):1}return d=="auto"?null:d},getOpacity:function(a){return $(a).getStyle("opacity")},setStyle:function(b,c){b=$(b);var e=b.style,a;if(Object.isString(c)){b.style.cssText+=";"+c;return c.include("opacity")?b.setOpacity(c.match(/opacity:\s*(\d?\.?\d*)/)[1]):b}for(var d in c){if(d=="opacity"){b.setOpacity(c[d])}else{e[(d=="float"||d=="cssFloat")?(Object.isUndefined(e.styleFloat)?"cssFloat":"styleFloat"):d]=c[d]}}return b},setOpacity:function(a,b){a=$(a);a.style.opacity=(b==1||b==="")?"":(b<0.00001)?0:b;return a},getDimensions:function(c){c=$(c);var g=c.getStyle("display");if(g!="none"&&g!=null){return{width:c.offsetWidth,height:c.offsetHeight}}var b=c.style;var f=b.visibility;var d=b.position;var a=b.display;b.visibility="hidden";b.position="absolute";b.display="block";var h=c.clientWidth;var e=c.clientHeight;b.display=a;b.position=d;b.visibility=f;return{width:h,height:e}},makePositioned:function(a){a=$(a);var b=Element.getStyle(a,"position");if(b=="static"||!b){a._madePositioned=true;a.style.position="relative";if(Prototype.Browser.Opera){a.style.top=0;a.style.left=0}}return a},undoPositioned:function(a){a=$(a);if(a._madePositioned){a._madePositioned=undefined;a.style.position=a.style.top=a.style.left=a.style.bottom=a.style.right=""}return a},makeClipping:function(a){a=$(a);if(a._overflow){return a}a._overflow=Element.getStyle(a,"overflow")||"auto";if(a._overflow!=="hidden"){a.style.overflow="hidden"}return a},undoClipping:function(a){a=$(a);if(!a._overflow){return a}a.style.overflow=a._overflow=="auto"?"":a._overflow;a._overflow=null;return a},cumulativeOffset:function(b){var a=0,c=0;do{a+=b.offsetTop||0;c+=b.offsetLeft||0;b=b.offsetParent}while(b);return Element._returnOffset(c,a)},positionedOffset:function(b){var a=0,d=0;do{a+=b.offsetTop||0;d+=b.offsetLeft||0;b=b.offsetParent;if(b){if(b.tagName.toUpperCase()=="BODY"){break}var c=Element.getStyle(b,"position");if(c!=="static"){break}}}while(b);return Element._returnOffset(d,a)},absolutize:function(b){b=$(b);if(b.getStyle("position")=="absolute"){return b}var d=b.positionedOffset();var f=d[1];var e=d[0];var c=b.clientWidth;var a=b.clientHeight;b._originalLeft=e-parseFloat(b.style.left||0);b._originalTop=f-parseFloat(b.style.top||0);b._originalWidth=b.style.width;b._originalHeight=b.style.height;b.style.position="absolute";b.style.top=f+"px";b.style.left=e+"px";b.style.width=c+"px";b.style.height=a+"px";return b},relativize:function(a){a=$(a);if(a.getStyle("position")=="relative"){return a}a.style.position="relative";var c=parseFloat(a.style.top||0)-(a._originalTop||0);var b=parseFloat(a.style.left||0)-(a._originalLeft||0);a.style.top=c+"px";a.style.left=b+"px";a.style.height=a._originalHeight;a.style.width=a._originalWidth;return a},cumulativeScrollOffset:function(b){var a=0,c=0;do{a+=b.scrollTop||0;c+=b.scrollLeft||0;b=b.parentNode}while(b);return Element._returnOffset(c,a)},getOffsetParent:function(b){b=$(b);var d=b.offsetParent,a=document.body,c=document.documentElement;if(d&&d!==c){return $(d)}if(d===c||b===c||b===a){return $(a)}while((b=b.parentNode)&&b!==a){if(Element.getStyle(b,"position")!="static"){return $(b)}}return $(a)},viewportOffset:function(d){d=$(d);var b=d,a=0,c=0;do{a+=b.offsetTop||0;c+=b.offsetLeft||0}while((b=b.getOffsetParent())!=document.body);b=d;do{if(!Prototype.Browser.Opera||(b.tagName&&(b.tagName.toUpperCase()=="BODY"))){a-=b.scrollTop||0;c-=b.scrollLeft||0}}while(b=b.parentNode);return Element._returnOffset(c,a)},clonePosition:function(b,d){var a=Object.extend({setLeft:true,setTop:true,setWidth:true,setHeight:true,offsetTop:0,offsetLeft:0},arguments[2]||{});d=$(d);var e=d.viewportOffset();b=$(b);var f=[0,0];var c=null;if(Element.getStyle(b,"position")=="absolute"){c=b.getOffsetParent();f=c.viewportOffset()}if(c==document.body){f[0]-=document.body.offsetLeft;f[1]-=document.body.offsetTop}if(a.setLeft){b.style.left=(e[0]-f[0]+a.offsetLeft)+"px"}if(a.setTop){b.style.top=(e[1]-f[1]+a.offsetTop)+"px"}if(a.setWidth){b.style.width=d.offsetWidth+"px"}if(a.setHeight){b.style.height=d.offsetHeight+"px"}return b}};Element.Methods.identify.counter=1;Object.extend(Element.Methods,{getElementsBySelector:Element.Methods.select,childElements:Element.Methods.immediateDescendants});Element._attributeTranslations={write:{names:{className:"class",htmlFor:"for"},values:{}}};if(Prototype.Browser.Opera){Element.Methods.getStyle=Element.Methods.getStyle.wrap(function(d,b,c){switch(c){case"left":case"top":case"right":case"bottom":if(d(b,"position")==="static"){return null}case"height":case"width":if(!Element.visible(b)){return null}var e=parseInt(d(b,c),10);if(e!==b["offset"+c.capitalize()]){return e+"px"}var a;if(c==="height"){a=["border-top-width","padding-top","padding-bottom","border-bottom-width"]}else{a=["border-left-width","padding-left","padding-right","border-right-width"]}return a.inject(e,function(f,g){var h=d(b,g);return h===null?f:f-parseInt(h,10)})+"px";default:return d(b,c)}});Element.Methods.readAttribute=Element.Methods.readAttribute.wrap(function(c,a,b){if(b==="title"){return a.title}return c(a,b)})}else{if(Prototype.Browser.IE){Element.Methods.getOffsetParent=Element.Methods.getOffsetParent.wrap(function(c,b){b=$(b);try{b.offsetParent}catch(f){return $(document.body)}var a=b.getStyle("position");if(a!=="static"){return c(b)}b.setStyle({position:"relative"});var d=c(b);b.setStyle({position:a});return d});$w("positionedOffset viewportOffset").each(function(a){Element.Methods[a]=Element.Methods[a].wrap(function(f,c){c=$(c);try{c.offsetParent}catch(h){return Element._returnOffset(0,0)}var b=c.getStyle("position");if(b!=="static"){return f(c)}var d=c.getOffsetParent();if(d&&d.getStyle("position")==="fixed"){d.setStyle({zoom:1})}c.setStyle({position:"relative"});var g=f(c);c.setStyle({position:b});return g})});Element.Methods.cumulativeOffset=Element.Methods.cumulativeOffset.wrap(function(b,a){try{a.offsetParent}catch(c){return Element._returnOffset(0,0)}return b(a)});Element.Methods.getStyle=function(a,b){a=$(a);b=(b=="float"||b=="cssFloat")?"styleFloat":b.camelize();var c=a.style[b];if(!c&&a.currentStyle){c=a.currentStyle[b]}if(b=="opacity"){if(c=(a.getStyle("filter")||"").match(/alpha\(opacity=(.*)\)/)){if(c[1]){return parseFloat(c[1])/100}}return 1}if(c=="auto"){if((b=="width"||b=="height")&&(a.getStyle("display")!="none")){return a["offset"+b.capitalize()]+"px"}return null}return c};Element.Methods.setOpacity=function(b,e){function f(g){return g.replace(/alpha\([^\)]*\)/gi,"")}b=$(b);var a=b.currentStyle;if((a&&!a.hasLayout)||(!a&&b.style.zoom=="normal")){b.style.zoom=1}var d=b.getStyle("filter"),c=b.style;if(e==1||e===""){(d=f(d))?c.filter=d:c.removeAttribute("filter");return b}else{if(e<0.00001){e=0}}c.filter=f(d)+"alpha(opacity="+(e*100)+")";return b};Element._attributeTranslations={read:{names:{"class":"className","for":"htmlFor"},values:{_getAttr:function(a,b){return a.getAttribute(b,2)},_getAttrNode:function(a,c){var b=a.getAttributeNode(c);return b?b.value:""},_getEv:function(a,b){b=a.getAttribute(b);return b?b.toString().slice(23,-2):null},_flag:function(a,b){return $(a).hasAttribute(b)?b:null},style:function(a){return a.style.cssText.toLowerCase()},title:function(a){return a.title}}}};Element._attributeTranslations.write={names:Object.extend({cellpadding:"cellPadding",cellspacing:"cellSpacing"},Element._attributeTranslations.read.names),values:{checked:function(a,b){a.checked=!!b},style:function(a,b){a.style.cssText=b?b:""}}};Element._attributeTranslations.has={};$w("colSpan rowSpan vAlign dateTime accessKey tabIndex encType maxLength readOnly longDesc frameBorder").each(function(a){Element._attributeTranslations.write.names[a.toLowerCase()]=a;Element._attributeTranslations.has[a.toLowerCase()]=a});(function(a){Object.extend(a,{href:a._getAttr,src:a._getAttr,type:a._getAttr,action:a._getAttrNode,disabled:a._flag,checked:a._flag,readonly:a._flag,multiple:a._flag,onload:a._getEv,onunload:a._getEv,onclick:a._getEv,ondblclick:a._getEv,onmousedown:a._getEv,onmouseup:a._getEv,onmouseover:a._getEv,onmousemove:a._getEv,onmouseout:a._getEv,onfocus:a._getEv,onblur:a._getEv,onkeypress:a._getEv,onkeydown:a._getEv,onkeyup:a._getEv,onsubmit:a._getEv,onreset:a._getEv,onselect:a._getEv,onchange:a._getEv})})(Element._attributeTranslations.read.values)}else{if(Prototype.Browser.Gecko&&/rv:1\.8\.0/.test(navigator.userAgent)){Element.Methods.setOpacity=function(a,b){a=$(a);a.style.opacity=(b==1)?0.999999:(b==="")?"":(b<0.00001)?0:b;return a}}else{if(Prototype.Browser.WebKit){Element.Methods.setOpacity=function(a,b){a=$(a);a.style.opacity=(b==1||b==="")?"":(b<0.00001)?0:b;if(b==1){if(a.tagName.toUpperCase()=="IMG"&&a.width){a.width++;a.width--}else{try{var d=document.createTextNode(" ");a.appendChild(d);a.removeChild(d)}catch(c){}}}return a};Element.Methods.cumulativeOffset=function(b){var a=0,c=0;do{a+=b.offsetTop||0;c+=b.offsetLeft||0;if(b.offsetParent==document.body){if(Element.getStyle(b,"position")=="absolute"){break}}b=b.offsetParent}while(b);return Element._returnOffset(c,a)}}}}}if(Prototype.Browser.IE||Prototype.Browser.Opera){Element.Methods.update=function(b,c){b=$(b);if(c&&c.toElement){c=c.toElement()}if(Object.isElement(c)){return b.update().insert(c)}c=Object.toHTML(c);var a=b.tagName.toUpperCase();if(a in Element._insertionTranslations.tags){$A(b.childNodes).each(function(d){b.removeChild(d)});Element._getContentFromAnonymousElement(a,c.stripScripts()).each(function(d){b.appendChild(d)})}else{b.innerHTML=c.stripScripts()}c.evalScripts.bind(c).defer();return b}}if("outerHTML"in document.createElement("div")){Element.Methods.replace=function(c,e){c=$(c);if(e&&e.toElement){e=e.toElement()}if(Object.isElement(e)){c.parentNode.replaceChild(e,c);return c}e=Object.toHTML(e);var d=c.parentNode,b=d.tagName.toUpperCase();if(Element._insertionTranslations.tags[b]){var f=c.next();var a=Element._getContentFromAnonymousElement(b,e.stripScripts());d.removeChild(c);if(f){a.each(function(g){d.insertBefore(g,f)})}else{a.each(function(g){d.appendChild(g)})}}else{c.outerHTML=e.stripScripts()}e.evalScripts.bind(e).defer();return c}}Element._returnOffset=function(b,c){var a=[b,c];a.left=b;a.top=c;return a};Element._getContentFromAnonymousElement=function(c,b){var d=new Element("div"),a=Element._insertionTranslations.tags[c];if(a){d.innerHTML=a[0]+b+a[1];a[2].times(function(){d=d.firstChild})}else{d.innerHTML=b}return $A(d.childNodes)};Element._insertionTranslations={before:function(a,b){a.parentNode.insertBefore(b,a)},top:function(a,b){a.insertBefore(b,a.firstChild)},bottom:function(a,b){a.appendChild(b)},after:function(a,b){a.parentNode.insertBefore(b,a.nextSibling)},tags:{TABLE:["<table>","</table>",1],TBODY:["<table><tbody>","</tbody></table>",2],TR:["<table><tbody><tr>","</tr></tbody></table>",3],TD:["<table><tbody><tr><td>","</td></tr></tbody></table>",4],SELECT:["<select>","</select>",1]}};(function(){Object.extend(this.tags,{THEAD:this.tags.TBODY,TFOOT:this.tags.TBODY,TH:this.tags.TD})}).call(Element._insertionTranslations);Element.Methods.Simulated={hasAttribute:function(a,c){c=Element._attributeTranslations.has[c]||c;var b=$(a).getAttributeNode(c);return!!(b&&b.specified)}};Element.Methods.ByTag={};Object.extend(Element,Element.Methods);if(!Prototype.BrowserFeatures.ElementExtensions&&document.createElement("div")["__proto__"]){window.HTMLElement={};window.HTMLElement.prototype=document.createElement("div")["__proto__"];Prototype.BrowserFeatures.ElementExtensions=true}Element.extend=(function(){if(Prototype.BrowserFeatures.SpecificElementExtensions){return Prototype.K}var a={},b=Element.Methods.ByTag;var c=Object.extend(function(f){if(!f||f._extendedByPrototype||f.nodeType!=1||f==window){return f}var d=Object.clone(a),e=f.tagName.toUpperCase(),h,g;if(b[e]){Object.extend(d,b[e])}for(h in d){g=d[h];if(Object.isFunction(g)&&!(h in f)){f[h]=g.methodize()}}f._extendedByPrototype=Prototype.emptyFunction;return f},{refresh:function(){if(!Prototype.BrowserFeatures.ElementExtensions){Object.extend(a,Element.Methods);Object.extend(a,Element.Methods.Simulated)}}});c.refresh();return c})();Element.hasAttribute=function(a,b){if(a.hasAttribute){return a.hasAttribute(b)}return Element.Methods.Simulated.hasAttribute(a,b)};Element.addMethods=function(c){var h=Prototype.BrowserFeatures,d=Element.Methods.ByTag;if(!c){Object.extend(Form,Form.Methods);Object.extend(Form.Element,Form.Element.Methods);Object.extend(Element.Methods.ByTag,{FORM:Object.clone(Form.Methods),INPUT:Object.clone(Form.Element.Methods),SELECT:Object.clone(Form.Element.Methods),TEXTAREA:Object.clone(Form.Element.Methods)})}if(arguments.length==2){var b=c;c=arguments[1]}if(!b){Object.extend(Element.Methods,c||{})}else{if(Object.isArray(b)){b.each(g)}else{g(b)}}function g(j){j=j.toUpperCase();if(!Element.Methods.ByTag[j]){Element.Methods.ByTag[j]={}}Object.extend(Element.Methods.ByTag[j],c)}function a(l,k,j){j=j||false;for(var n in l){var m=l[n];if(!Object.isFunction(m)){continue}if(!j||!(n in k)){k[n]=m.methodize()}}}function e(l){var j;var k={OPTGROUP:"OptGroup",TEXTAREA:"TextArea",P:"Paragraph",FIELDSET:"FieldSet",UL:"UList",OL:"OList",DL:"DList",DIR:"Directory",H1:"Heading",H2:"Heading",H3:"Heading",H4:"Heading",H5:"Heading",H6:"Heading",Q:"Quote",INS:"Mod",DEL:"Mod",A:"Anchor",IMG:"Image",CAPTION:"TableCaption",COL:"TableCol",COLGROUP:"TableCol",THEAD:"TableSection",TFOOT:"TableSection",TBODY:"TableSection",TR:"TableRow",TH:"TableCell",TD:"TableCell",FRAMESET:"FrameSet",IFRAME:"IFrame"};if(k[l]){j="HTML"+k[l]+"Element"}if(window[j]){return window[j]}j="HTML"+l+"Element";if(window[j]){return window[j]}j="HTML"+l.capitalize()+"Element";if(window[j]){return window[j]}window[j]={};window[j].prototype=document.createElement(l)["__proto__"];return window[j]}if(h.ElementExtensions){a(Element.Methods,HTMLElement.prototype);a(Element.Methods.Simulated,HTMLElement.prototype,true)}if(h.SpecificElementExtensions){for(var i in Element.Methods.ByTag){var f=e(i);if(Object.isUndefined(f)){continue}a(d[i],f.prototype)}}Object.extend(Element,Element.Methods);delete Element.ByTag;if(Element.extend.refresh){Element.extend.refresh()}Element.cache={}};document.viewport={getDimensions:function(){var a={},b=Prototype.Browser;$w("width height").each(function(e){var c=e.capitalize();if(b.WebKit&&!document.evaluate){a[e]=self["inner"+c]}else{if(b.Opera&&parseFloat(window.opera.version())<9.5){a[e]=document.body["client"+c]}else{a[e]=document.documentElement["client"+c]}}});return a},getWidth:function(){return this.getDimensions().width},getHeight:function(){return this.getDimensions().height},getScrollOffsets:function(){return Element._returnOffset(window.pageXOffset||document.documentElement.scrollLeft||document.body.scrollLeft,window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop)}};var Selector=Class.create({initialize:function(a){this.expression=a.strip();if(this.shouldUseSelectorsAPI()){this.mode="selectorsAPI"}else{if(this.shouldUseXPath()){this.mode="xpath";this.compileXPathMatcher()}else{this.mode="normal";this.compileMatcher()}}},shouldUseXPath:function(){if(!Prototype.BrowserFeatures.XPath){return false}var a=this.expression;if(Prototype.Browser.WebKit&&(a.include("-of-type")||a.include(":empty"))){return false}if((/(\[[\w-]*?:|:checked)/).test(a)){return false}return true},shouldUseSelectorsAPI:function(){if(!Prototype.BrowserFeatures.SelectorsAPI){return false}if(!Selector._div){Selector._div=new Element("div")}try{Selector._div.querySelector(this.expression)}catch(a){return false}return true},compileMatcher:function(){var e=this.expression,ps=Selector.patterns,h=Selector.handlers,c=Selector.criteria,le,p,m;if(Selector._cache[e]){this.matcher=Selector._cache[e];return}this.matcher=["this.matcher = function(root) {","var r = root, h = Selector.handlers, c = false, n;"];while(e&&le!=e&&(/\S/).test(e)){le=e;for(var i in ps){p=ps[i];if(m=e.match(p)){this.matcher.push(Object.isFunction(c[i])?c[i](m):new Template(c[i]).evaluate(m));e=e.replace(m[0],"");break}}}this.matcher.push("return h.unique(n);\n}");eval(this.matcher.join("\n"));Selector._cache[this.expression]=this.matcher},compileXPathMatcher:function(){var f=this.expression,g=Selector.patterns,b=Selector.xpath,d,a;if(Selector._cache[f]){this.xpath=Selector._cache[f];return}this.matcher=[".//*"];while(f&&d!=f&&(/\S/).test(f)){d=f;for(var c in g){if(a=f.match(g[c])){this.matcher.push(Object.isFunction(b[c])?b[c](a):new Template(b[c]).evaluate(a));f=f.replace(a[0],"");break}}}this.xpath=this.matcher.join("");Selector._cache[this.expression]=this.xpath},findElements:function(a){a=a||document;var c=this.expression,b;switch(this.mode){case"selectorsAPI":if(a!==document){var d=a.id,f=$(a).identify();c="#"+f+" "+c}b=$A(a.querySelectorAll(c)).map(Element.extend);a.id=d;return b;case"xpath":return document._getElementsByXPath(this.xpath,a);default:return this.matcher(a)}},match:function(j){this.tokens=[];var o=this.expression,a=Selector.patterns,f=Selector.assertions;var b,d,g;while(o&&b!==o&&(/\S/).test(o)){b=o;for(var k in a){d=a[k];if(g=o.match(d)){if(f[k]){this.tokens.push([k,Object.clone(g)]);o=o.replace(g[0],"")}else{return this.findElements(document).include(j)}}}}var n=true,c,l;for(var k=0,h;h=this.tokens[k];k++){c=h[0],l=h[1];if(!Selector.assertions[c](j,l)){n=false;break}}return n},toString:function(){return this.expression},inspect:function(){return"#<Selector:"+this.expression.inspect()+">"}});Object.extend(Selector,{_cache:{},xpath:{descendant:"//*",child:"/*",adjacent:"/following-sibling::*[1]",laterSibling:"/following-sibling::*",tagName:function(a){if(a[1]=="*"){return""}return"[local-name()='"+a[1].toLowerCase()+"' or local-name()='"+a[1].toUpperCase()+"']"},className:"[contains(concat(' ', @class, ' '), ' #{1} ')]",id:"[@id='#{1}']",attrPresence:function(a){a[1]=a[1].toLowerCase();return new Template("[@#{1}]").evaluate(a)},attr:function(a){a[1]=a[1].toLowerCase();a[3]=a[5]||a[6];return new Template(Selector.xpath.operators[a[2]]).evaluate(a)},pseudo:function(a){var b=Selector.xpath.pseudos[a[1]];if(!b){return""}if(Object.isFunction(b)){return b(a)}return new Template(Selector.xpath.pseudos[a[1]]).evaluate(a)},operators:{"=":"[@#{1}='#{3}']","!=":"[@#{1}!='#{3}']","^=":"[starts-with(@#{1}, '#{3}')]","$=":"[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']","*=":"[contains(@#{1}, '#{3}')]","~=":"[contains(concat(' ', @#{1}, ' '), ' #{3} ')]","|=":"[contains(concat('-', @#{1}, '-'), '-#{3}-')]"},pseudos:{"first-child":"[not(preceding-sibling::*)]","last-child":"[not(following-sibling::*)]","only-child":"[not(preceding-sibling::* or following-sibling::*)]",empty:"[count(*) = 0 and (count(text()) = 0)]",checked:"[@checked]",disabled:"[(@disabled) and (@type!='hidden')]",enabled:"[not(@disabled) and (@type!='hidden')]",not:function(b){var j=b[6],h=Selector.patterns,a=Selector.xpath,f,c;var g=[];while(j&&f!=j&&(/\S/).test(j)){f=j;for(var d in h){if(b=j.match(h[d])){c=Object.isFunction(a[d])?a[d](b):new Template(a[d]).evaluate(b);g.push("("+c.substring(1,c.length-1)+")");j=j.replace(b[0],"");break}}}return"[not("+g.join(" and ")+")]"},"nth-child":function(a){return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ",a)},"nth-last-child":function(a){return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ",a)},"nth-of-type":function(a){return Selector.xpath.pseudos.nth("position() ",a)},"nth-last-of-type":function(a){return Selector.xpath.pseudos.nth("(last() + 1 - position()) ",a)},"first-of-type":function(a){a[6]="1";return Selector.xpath.pseudos["nth-of-type"](a)},"last-of-type":function(a){a[6]="1";return Selector.xpath.pseudos["nth-last-of-type"](a)},"only-of-type":function(a){var b=Selector.xpath.pseudos;return b["first-of-type"](a)+b["last-of-type"](a)},nth:function(g,e){var h,i=e[6],d;if(i=="even"){i="2n+0"}if(i=="odd"){i="2n+1"}if(h=i.match(/^(\d+)$/)){return"["+g+"= "+h[1]+"]"}if(h=i.match(/^(-?\d*)?n(([+-])(\d+))?/)){if(h[1]=="-"){h[1]=-1}var f=h[1]?Number(h[1]):1;var c=h[2]?Number(h[2]):0;d="[((#{fragment} - #{b}) mod #{a} = 0) and ((#{fragment} - #{b}) div #{a} >= 0)]";return new Template(d).evaluate({fragment:g,a:f,b:c})}}}},criteria:{tagName:'n = h.tagName(n, r, "#{1}", c);      c = false;',className:'n = h.className(n, r, "#{1}", c);    c = false;',id:'n = h.id(n, r, "#{1}", c);           c = false;',attrPresence:'n = h.attrPresence(n, r, "#{1}", c); c = false;',attr:function(a){a[3]=(a[5]||a[6]);return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(a)},pseudo:function(a){if(a[6]){a[6]=a[6].replace(/"/g,'\\"')}return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(a)},descendant:'c = "descendant";',child:'c = "child";',adjacent:'c = "adjacent";',laterSibling:'c = "laterSibling";'},patterns:{laterSibling:/^\s*~\s*/,child:/^\s*>\s*/,adjacent:/^\s*\+\s*/,descendant:/^\s/,tagName:/^\s*(\*|[\w\-]+)(\b|$)?/,id:/^#([\w\-\*]+)(\b|$)/,className:/^\.([\w\-\*]+)(\b|$)/,pseudo:/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/,attrPresence:/^\[((?:[\w]+:)?[\w]+)\]/,attr:/\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/},assertions:{tagName:function(a,b){return b[1].toUpperCase()==a.tagName.toUpperCase()},className:function(a,b){return Element.hasClassName(a,b[1])},id:function(a,b){return a.id===b[1]},attrPresence:function(a,b){return Element.hasAttribute(a,b[1])},attr:function(b,c){var a=Element.readAttribute(b,c[1]);return a&&Selector.operators[c[2]](a,c[5]||c[6])}},handlers:{concat:function(d,c){for(var e=0,f;f=c[e];e++){d.push(f)}return d},mark:function(a){var d=Prototype.emptyFunction;for(var b=0,c;c=a[b];b++){c._countedByPrototype=d}return a},unmark:function(a){for(var b=0,c;c=a[b];b++){c._countedByPrototype=undefined}return a},index:function(a,d,g){a._countedByPrototype=Prototype.emptyFunction;if(d){for(var b=a.childNodes,e=b.length-1,c=1;e>=0;e--){var f=b[e];if(f.nodeType==1&&(!g||f._countedByPrototype)){f.nodeIndex=c++}}}else{for(var e=0,c=1,b=a.childNodes;f=b[e];e++){if(f.nodeType==1&&(!g||f._countedByPrototype)){f.nodeIndex=c++}}}},unique:function(b){if(b.length==0){return b}var d=[],e;for(var c=0,a=b.length;c<a;c++){if(!(e=b[c])._countedByPrototype){e._countedByPrototype=Prototype.emptyFunction;d.push(Element.extend(e))}}return Selector.handlers.unmark(d)},descendant:function(a){var d=Selector.handlers;for(var c=0,b=[],e;e=a[c];c++){d.concat(b,e.getElementsByTagName("*"))}return b},child:function(a){var e=Selector.handlers;for(var d=0,c=[],f;f=a[d];d++){for(var b=0,g;g=f.childNodes[b];b++){if(g.nodeType==1&&g.tagName!="!"){c.push(g)}}}return c},adjacent:function(a){for(var c=0,b=[],e;e=a[c];c++){var d=this.nextElementSibling(e);if(d){b.push(d)}}return b},laterSibling:function(a){var d=Selector.handlers;for(var c=0,b=[],e;e=a[c];c++){d.concat(b,Element.nextSiblings(e))}return b},nextElementSibling:function(a){while(a=a.nextSibling){if(a.nodeType==1){return a}}return null},previousElementSibling:function(a){while(a=a.previousSibling){if(a.nodeType==1){return a}}return null},tagName:function(a,j,c,b){var k=c.toUpperCase();var e=[],g=Selector.handlers;if(a){if(b){if(b=="descendant"){for(var f=0,d;d=a[f];f++){g.concat(e,d.getElementsByTagName(c))}return e}else{a=this[b](a)}if(c=="*"){return a}}for(var f=0,d;d=a[f];f++){if(d.tagName.toUpperCase()===k){e.push(d)}}return e}else{return j.getElementsByTagName(c)}},id:function(b,a,j,f){var g=$(j),d=Selector.handlers;if(!g){return[]}if(!b&&a==document){return[g]}if(b){if(f){if(f=="child"){for(var c=0,e;e=b[c];c++){if(g.parentNode==e){return[g]}}}else{if(f=="descendant"){for(var c=0,e;e=b[c];c++){if(Element.descendantOf(g,e)){return[g]}}}else{if(f=="adjacent"){for(var c=0,e;e=b[c];c++){if(Selector.handlers.previousElementSibling(g)==e){return[g]}}}else{b=d[f](b)}}}}for(var c=0,e;e=b[c];c++){if(e==g){return[g]}}return[]}return(g&&Element.descendantOf(g,a))?[g]:[]},className:function(b,a,c,d){if(b&&d){b=this[d](b)}return Selector.handlers.byClassName(b,a,c)},byClassName:function(c,b,f){if(!c){c=Selector.handlers.descendant([b])}var h=" "+f+" ";for(var e=0,d=[],g,a;g=c[e];e++){a=g.className;if(a.length==0){continue}if(a==f||(" "+a+" ").include(h)){d.push(g)}}return d},attrPresence:function(c,b,a,g){if(!c){c=b.getElementsByTagName("*")}if(c&&g){c=this[g](c)}var e=[];for(var d=0,f;f=c[d];d++){if(Element.hasAttribute(f,a)){e.push(f)}}return e},attr:function(a,j,h,k,c,b){if(!a){a=j.getElementsByTagName("*")}if(a&&b){a=this[b](a)}var l=Selector.operators[c],f=[];for(var e=0,d;d=a[e];e++){var g=Element.readAttribute(d,h);if(g===null){continue}if(l(g,k)){f.push(d)}}return f},pseudo:function(b,c,e,a,d){if(b&&d){b=this[d](b)}if(!b){b=a.getElementsByTagName("*")}return Selector.pseudos[c](b,e,a)}},pseudos:{"first-child":function(b,f,a){for(var d=0,c=[],e;e=b[d];d++){if(Selector.handlers.previousElementSibling(e)){continue}c.push(e)}return c},"last-child":function(b,f,a){for(var d=0,c=[],e;e=b[d];d++){if(Selector.handlers.nextElementSibling(e)){continue}c.push(e)}return c},"only-child":function(b,g,a){var e=Selector.handlers;for(var d=0,c=[],f;f=b[d];d++){if(!e.previousElementSibling(f)&&!e.nextElementSibling(f)){c.push(f)}}return c},"nth-child":function(b,c,a){return Selector.pseudos.nth(b,c,a)},"nth-last-child":function(b,c,a){return Selector.pseudos.nth(b,c,a,true)},"nth-of-type":function(b,c,a){return Selector.pseudos.nth(b,c,a,false,true)},"nth-last-of-type":function(b,c,a){return Selector.pseudos.nth(b,c,a,true,true)},"first-of-type":function(b,c,a){return Selector.pseudos.nth(b,"1",a,false,true)},"last-of-type":function(b,c,a){return Selector.pseudos.nth(b,"1",a,true,true)},"only-of-type":function(b,d,a){var c=Selector.pseudos;return c["last-of-type"](c["first-of-type"](b,d,a),d,a)},getIndices:function(d,c,e){if(d==0){return c>0?[c]:[]}return $R(1,e).inject([],function(a,b){if(0==(b-c)%d&&(b-c)/d>=0){a.push(b)}return a})},nth:function(c,s,u,r,e){if(c.length==0){return[]}if(s=="even"){s="2n+0"}if(s=="odd"){s="2n+1"}var q=Selector.handlers,p=[],d=[],g;q.mark(c);for(var o=0,f;f=c[o];o++){if(!f.parentNode._countedByPrototype){q.index(f.parentNode,r,e);d.push(f.parentNode)}}if(s.match(/^\d+$/)){s=Number(s);for(var o=0,f;f=c[o];o++){if(f.nodeIndex==s){p.push(f)}}}else{if(g=s.match(/^(-?\d*)?n(([+-])(\d+))?/)){if(g[1]=="-"){g[1]=-1}var v=g[1]?Number(g[1]):1;var t=g[2]?Number(g[2]):0;var w=Selector.pseudos.getIndices(v,t,c.length);for(var o=0,f,k=w.length;f=c[o];o++){for(var n=0;n<k;n++){if(f.nodeIndex==w[n]){p.push(f)}}}}}q.unmark(c);q.unmark(d);return p},empty:function(b,f,a){for(var d=0,c=[],e;e=b[d];d++){if(e.tagName=="!"||e.firstChild){continue}c.push(e)}return c},not:function(a,d,k){var g=Selector.handlers,l,c;var j=new Selector(d).findElements(k);g.mark(j);for(var f=0,e=[],b;b=a[f];f++){if(!b._countedByPrototype){e.push(b)}}g.unmark(j);return e},enabled:function(b,f,a){for(var d=0,c=[],e;e=b[d];d++){if(!e.disabled&&(!e.type||e.type!=="hidden")){c.push(e)}}return c},disabled:function(b,f,a){for(var d=0,c=[],e;e=b[d];d++){if(e.disabled){c.push(e)}}return c},checked:function(b,f,a){for(var d=0,c=[],e;e=b[d];d++){if(e.checked){c.push(e)}}return c}},operators:{"=":function(b,a){return b==a},"!=":function(b,a){return b!=a},"^=":function(b,a){return b==a||b&&b.startsWith(a)},"$=":function(b,a){return b==a||b&&b.endsWith(a)},"*=":function(b,a){return b==a||b&&b.include(a)},"$=":function(b,a){return b.endsWith(a)},"*=":function(b,a){return b.include(a)},"~=":function(b,a){return(" "+b+" ").include(" "+a+" ")},"|=":function(b,a){return("-"+(b||"").toUpperCase()+"-").include("-"+(a||"").toUpperCase()+"-")}},split:function(b){var a=[];b.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/,function(c){a.push(c[1].strip())});return a},matchElements:function(f,g){var e=$$(g),d=Selector.handlers;d.mark(e);for(var c=0,b=[],a;a=f[c];c++){if(a._countedByPrototype){b.push(a)}}d.unmark(e);return b},findElement:function(b,c,a){if(Object.isNumber(c)){a=c;c=false}return Selector.matchElements(b,c||"*")[a||0]},findChildElements:function(e,g){g=Selector.split(g.join(","));var d=[],f=Selector.handlers;for(var c=0,b=g.length,a;c<b;c++){a=new Selector(g[c].strip());f.concat(d,a.findElements(e))}return(b>1)?f.unique(d):d}});if(Prototype.Browser.IE){Object.extend(Selector.handlers,{concat:function(d,c){for(var e=0,f;f=c[e];e++){if(f.tagName!=="!"){d.push(f)}}return d},unmark:function(a){for(var b=0,c;c=a[b];b++){c.removeAttribute("_countedByPrototype")}return a}})}function $$(){return Selector.findChildElements(document,$A(arguments))}var Form={reset:function(a){$(a).reset();return a},serializeElements:function(g,b){if(typeof b!="object"){b={hash:!!b}}else{if(Object.isUndefined(b.hash)){b.hash=true}}var c,f,a=false,e=b.submit;var d=g.inject({},function(h,i){if(!i.disabled&&i.name){c=i.name;f=$(i).getValue();if(f!=null&&i.type!="file"&&(i.type!="submit"||(!a&&e!==false&&(!e||c==e)&&(a=true)))){if(c in h){if(!Object.isArray(h[c])){h[c]=[h[c]]}h[c].push(f)}else{h[c]=f}}}return h});return b.hash?d:Object.toQueryString(d)}};Form.Methods={serialize:function(b,a){return Form.serializeElements(Form.getElements(b),a)},getElements:function(a){return $A($(a).getElementsByTagName("*")).inject([],function(b,c){if(Form.Element.Serializers[c.tagName.toLowerCase()]){b.push(Element.extend(c))}return b})},getInputs:function(g,c,d){g=$(g);var a=g.getElementsByTagName("input");if(!c&&!d){return $A(a).map(Element.extend)}for(var e=0,h=[],f=a.length;e<f;e++){var b=a[e];if((c&&b.type!=c)||(d&&b.name!=d)){continue}h.push(Element.extend(b))}return h},disable:function(a){a=$(a);Form.getElements(a).invoke("disable");return a},enable:function(a){a=$(a);Form.getElements(a).invoke("enable");return a},findFirstElement:function(b){var c=$(b).getElements().findAll(function(d){return"hidden"!=d.type&&!d.disabled});var a=c.findAll(function(d){return d.hasAttribute("tabIndex")&&d.tabIndex>=0}).sortBy(function(d){return d.tabIndex}).first();return a?a:c.find(function(d){return["input","select","textarea"].include(d.tagName.toLowerCase())})},focusFirstElement:function(a){a=$(a);a.findFirstElement().activate();return a},request:function(b,a){b=$(b),a=Object.clone(a||{});var d=a.parameters,c=b.readAttribute("action")||"";if(c.blank()){c=window.location.href}a.parameters=b.serialize(true);if(d){if(Object.isString(d)){d=d.toQueryParams()}Object.extend(a.parameters,d)}if(b.hasAttribute("method")&&!a.method){a.method=b.method}return new Ajax.Request(c,a)}};Form.Element={focus:function(a){$(a).focus();return a},select:function(a){$(a).select();return a}};Form.Element.Methods={serialize:function(a){a=$(a);if(!a.disabled&&a.name){var b=a.getValue();if(b!=undefined){var c={};c[a.name]=b;return Object.toQueryString(c)}}return""},getValue:function(a){a=$(a);var b=a.tagName.toLowerCase();return Form.Element.Serializers[b](a)},setValue:function(a,b){a=$(a);var c=a.tagName.toLowerCase();Form.Element.Serializers[c](a,b);return a},clear:function(a){$(a).value="";return a},present:function(a){return $(a).value!=""},activate:function(a){a=$(a);try{a.focus();if(a.select&&(a.tagName.toLowerCase()!="input"||!["button","reset","submit"].include(a.type))){a.select()}}catch(b){}return a},disable:function(a){a=$(a);a.disabled=true;return a},enable:function(a){a=$(a);a.disabled=false;return a}};var Field=Form.Element;var $F=Form.Element.Methods.getValue;Form.Element.Serializers={input:function(a,b){switch(a.type.toLowerCase()){case"checkbox":case"radio":return Form.Element.Serializers.inputSelector(a,b);default:return Form.Element.Serializers.textarea(a,b)}},inputSelector:function(a,b){if(Object.isUndefined(b)){return a.checked?a.value:null}else{a.checked=!!b}},textarea:function(a,b){if(Object.isUndefined(b)){return a.value}else{a.value=b}},select:function(c,f){if(Object.isUndefined(f)){return this[c.type=="select-one"?"selectOne":"selectMany"](c)}else{var b,d,g=!Object.isArray(f);for(var a=0,e=c.length;a<e;a++){b=c.options[a];d=this.optionValue(b);if(g){if(d==f){b.selected=true;return}}else{b.selected=f.include(d)}}}},selectOne:function(b){var a=b.selectedIndex;return a>=0?this.optionValue(b.options[a]):null},selectMany:function(d){var a,e=d.length;if(!e){return null}for(var c=0,a=[];c<e;c++){var b=d.options[c];if(b.selected){a.push(this.optionValue(b))}}return a},optionValue:function(a){return Element.extend(a).hasAttribute("value")?a.value:a.text}};Abstract.TimedObserver=Class.create(PeriodicalExecuter,{initialize:function($super,a,b,c){$super(c,b);this.element=$(a);this.lastValue=this.getValue()},execute:function(){var a=this.getValue();if(Object.isString(this.lastValue)&&Object.isString(a)?this.lastValue!=a:String(this.lastValue)!=String(a)){this.callback(this.element,a);this.lastValue=a}}});Form.Element.Observer=Class.create(Abstract.TimedObserver,{getValue:function(){return Form.Element.getValue(this.element)}});Form.Observer=Class.create(Abstract.TimedObserver,{getValue:function(){return Form.serialize(this.element)}});Abstract.EventObserver=Class.create({initialize:function(a,b){this.element=$(a);this.callback=b;this.lastValue=this.getValue();if(this.element.tagName.toLowerCase()=="form"){this.registerFormCallbacks()}else{this.registerCallback(this.element)}},onElementEvent:function(){var a=this.getValue();if(this.lastValue!=a){this.callback(this.element,a);this.lastValue=a}},registerFormCallbacks:function(){Form.getElements(this.element).each(this.registerCallback,this)},registerCallback:function(a){if(a.type){switch(a.type.toLowerCase()){case"checkbox":case"radio":Event.observe(a,"click",this.onElementEvent.bind(this));break;default:Event.observe(a,"change",this.onElementEvent.bind(this));break}}}});Form.Element.EventObserver=Class.create(Abstract.EventObserver,{getValue:function(){return Form.Element.getValue(this.element)}});Form.EventObserver=Class.create(Abstract.EventObserver,{getValue:function(){return Form.serialize(this.element)}});if(!window.Event){var Event={}}Object.extend(Event,{KEY_BACKSPACE:8,KEY_TAB:9,KEY_RETURN:13,KEY_ESC:27,KEY_LEFT:37,KEY_UP:38,KEY_RIGHT:39,KEY_DOWN:40,KEY_DELETE:46,KEY_HOME:36,KEY_END:35,KEY_PAGEUP:33,KEY_PAGEDOWN:34,KEY_INSERT:45,cache:{},relatedTarget:function(b){var a;switch(b.type){case"mouseover":a=b.fromElement;break;case"mouseout":a=b.toElement;break;default:return null}return Element.extend(a)}});Event.Methods=(function(){var a;if(Prototype.Browser.IE){var b={0:1,1:4,2:2};a=function(d,c){return d.button==b[c]}}else{if(Prototype.Browser.WebKit){a=function(d,c){switch(c){case 0:return d.which==1&&!d.metaKey;case 1:return d.which==1&&d.metaKey;default:return false}}}else{a=function(d,c){return d.which?(d.which===c+1):(d.button===c)}}}return{isLeftClick:function(c){return a(c,0)},isMiddleClick:function(c){return a(c,1)},isRightClick:function(c){return a(c,2)},element:function(e){e=Event.extend(e);var d=e.target,c=e.type,f=e.currentTarget;if(f&&f.tagName){if(c==="load"||c==="error"||(c==="click"&&f.tagName.toLowerCase()==="input"&&f.type==="radio")){d=f}}if(d.nodeType==Node.TEXT_NODE){d=d.parentNode}return Element.extend(d)},findElement:function(d,f){var c=Event.element(d);if(!f){return c}var e=[c].concat(c.ancestors());return Selector.findElement(e,f,0)},pointer:function(e){var d=document.documentElement,c=document.body||{scrollLeft:0,scrollTop:0};return{x:e.pageX||(e.clientX+(d.scrollLeft||c.scrollLeft)-(d.clientLeft||0)),y:e.pageY||(e.clientY+(d.scrollTop||c.scrollTop)-(d.clientTop||0))}},pointerX:function(c){return Event.pointer(c).x},pointerY:function(c){return Event.pointer(c).y},stop:function(c){Event.extend(c);c.preventDefault();c.stopPropagation();c.stopped=true}}})();Event.extend=(function(){var a=Object.keys(Event.Methods).inject({},function(b,c){b[c]=Event.Methods[c].methodize();return b});if(Prototype.Browser.IE){Object.extend(a,{stopPropagation:function(){this.cancelBubble=true},preventDefault:function(){this.returnValue=false},inspect:function(){return"[object Event]"}});return function(b){if(!b){return false}if(b._extendedByPrototype){return b}b._extendedByPrototype=Prototype.emptyFunction;var c=Event.pointer(b);Object.extend(b,{target:b.srcElement,relatedTarget:Event.relatedTarget(b),pageX:c.x,pageY:c.y});return Object.extend(b,a)}}else{Event.prototype=Event.prototype||document.createEvent("HTMLEvents")["__proto__"];Object.extend(Event.prototype,a);return Prototype.K}})();Object.extend(Event,(function(){var b=Event.cache;function c(j){if(j._prototypeEventID){return j._prototypeEventID[0]}arguments.callee.id=arguments.callee.id||1;return j._prototypeEventID=[++arguments.callee.id]}function g(j){if(j&&j.include(":")){return"dataavailable"}return j}function a(j){return b[j]=b[j]||{}}function f(l,j){var k=a(l);return k[j]=k[j]||[]}function h(k,j,l){var o=c(k);var n=f(o,j);if(n.pluck("handler").include(l)){return false}var m=function(p){if(!Event||!Event.extend||(p.eventName&&p.eventName!=j)){return false}Event.extend(p);l.call(k,p)};m.handler=l;n.push(m);return m}function i(m,j,k){var l=f(m,j);return l.find(function(n){return n.handler==k})}function d(m,j,k){var l=a(m);if(!l[j]){return false}l[j]=l[j].without(i(m,j,k))}function e(){for(var k in b){for(var j in b[k]){b[k][j]=null}}}if(window.attachEvent){window.attachEvent("onunload",e)}if(Prototype.Browser.WebKit){window.addEventListener("unload",Prototype.emptyFunction,false)}return{observe:function(l,j,m){l=$(l);var k=g(j);var n=h(l,j,m);if(!n){return l}if(l.addEventListener){l.addEventListener(k,n,false)}else{l.attachEvent("on"+k,n)}return l},stopObserving:function(l,j,m){l=$(l);var o=c(l),k=g(j);if(!m&&j){f(o,j).each(function(p){l.stopObserving(j,p.handler)});return l}else{if(!j){Object.keys(a(o)).each(function(p){l.stopObserving(p)});return l}}var n=i(o,j,m);if(!n){return l}if(l.removeEventListener){l.removeEventListener(k,n,false)}else{l.detachEvent("on"+k,n)}d(o,j,m);return l},fire:function(l,k,j){l=$(l);if(l==document&&document.createEvent&&!l.dispatchEvent){l=document.documentElement}var m;if(document.createEvent){m=document.createEvent("HTMLEvents");m.initEvent("dataavailable",true,true)}else{m=document.createEventObject();m.eventType="ondataavailable"}m.eventName=k;m.memo=j||{};if(document.createEvent){l.dispatchEvent(m)}else{l.fireEvent(m.eventType,m)}return Event.extend(m)}}})());Object.extend(Event,Event.Methods);Element.addMethods({fire:Event.fire,observe:Event.observe,stopObserving:Event.stopObserving});Object.extend(document,{fire:Element.Methods.fire.methodize(),observe:Element.Methods.observe.methodize(),stopObserving:Element.Methods.stopObserving.methodize(),loaded:false});(function(){var b;function a(){if(document.loaded){return}if(b){window.clearInterval(b)}document.fire("dom:loaded");document.loaded=true}if(document.addEventListener){if(Prototype.Browser.WebKit){b=window.setInterval(function(){if(/loaded|complete/.test(document.readyState)){a()}},0);Event.observe(window,"load",a)}else{document.addEventListener("DOMContentLoaded",a,false)}}else{document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>");$("__onDOMContentLoaded").onreadystatechange=function(){if(this.readyState=="complete"){this.onreadystatechange=null;a()}}}})();Hash.toQueryString=Object.toQueryString;var Toggle={display:Element.toggle};Element.Methods.childOf=Element.Methods.descendantOf;var Insertion={Before:function(a,b){return Element.insert(a,{before:b})},Top:function(a,b){return Element.insert(a,{top:b})},Bottom:function(a,b){return Element.insert(a,{bottom:b})},After:function(a,b){return Element.insert(a,{after:b})}};var $continue=new Error('"throw $continue" is deprecated, use "return" instead');var Position={includeScrollOffsets:false,prepare:function(){this.deltaX=window.pageXOffset||document.documentElement.scrollLeft||document.body.scrollLeft||0;this.deltaY=window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0},within:function(b,a,c){if(this.includeScrollOffsets){return this.withinIncludingScrolloffsets(b,a,c)}this.xcomp=a;this.ycomp=c;this.offset=Element.cumulativeOffset(b);return(c>=this.offset[1]&&c<this.offset[1]+b.offsetHeight&&a>=this.offset[0]&&a<this.offset[0]+b.offsetWidth)},withinIncludingScrolloffsets:function(b,a,d){var c=Element.cumulativeScrollOffset(b);this.xcomp=a+c[0]-this.deltaX;this.ycomp=d+c[1]-this.deltaY;this.offset=Element.cumulativeOffset(b);return(this.ycomp>=this.offset[1]&&this.ycomp<this.offset[1]+b.offsetHeight&&this.xcomp>=this.offset[0]&&this.xcomp<this.offset[0]+b.offsetWidth)},overlap:function(b,a){if(!b){return 0}if(b=="vertical"){return((this.offset[1]+a.offsetHeight)-this.ycomp)/a.offsetHeight}if(b=="horizontal"){return((this.offset[0]+a.offsetWidth)-this.xcomp)/a.offsetWidth}},cumulativeOffset:Element.Methods.cumulativeOffset,positionedOffset:Element.Methods.positionedOffset,absolutize:function(a){Position.prepare();return Element.absolutize(a)},relativize:function(a){Position.prepare();return Element.relativize(a)},realOffset:Element.Methods.cumulativeScrollOffset,offsetParent:Element.Methods.getOffsetParent,page:Element.Methods.viewportOffset,clone:function(b,c,a){a=a||{};return Element.clonePosition(c,b,a)}};if(!document.getElementsByClassName){document.getElementsByClassName=function(b){function a(c){return c.blank()?null:"[contains(concat(' ', @class, ' '), ' "+c+" ')]"}b.getElementsByClassName=Prototype.BrowserFeatures.XPath?function(c,e){e=e.toString().strip();var d=/\s/.test(e)?$w(e).map(a).join(""):a(e);return d?document._getElementsByXPath(".//*"+d,c):[]}:function(e,f){f=f.toString().strip();var g=[],h=(/\s/.test(f)?$w(f):null);if(!h&&!f){return g}var c=$(e).getElementsByTagName("*");f=" "+f+" ";for(var d=0,k,j;k=c[d];d++){if(k.className&&(j=" "+k.className+" ")&&(j.include(f)||(h&&h.all(function(i){return!i.toString().blank()&&j.include(" "+i+" ")})))){g.push(Element.extend(k))}}return g};return function(d,c){return $(c||document.body).getElementsByClassName(d)}}(Element.Methods)}Element.ClassNames=Class.create();Element.ClassNames.prototype={initialize:function(a){this.element=$(a)},_each:function(a){this.element.className.split(/\s+/).select(function(b){return b.length>0})._each(a)},set:function(a){this.element.className=a},add:function(a){if(this.include(a)){return}this.set($A(this).concat(a).join(" "))},remove:function(a){if(!this.include(a)){return}this.set($A(this).without(a).join(" "))},toString:function(){return $A(this).join(" ")}};Object.extend(Element.ClassNames.prototype,Enumerable);Element.addMethods();
\ No newline at end of file
diff --git a/js/revlog.js b/js/revlog.js
new file mode 100644
index 0000000..ec85793
--- /dev/null
+++ b/js/revlog.js
@@ -0,0 +1 @@
+var revlog_selected=null;var isMSIE=/*@cc_on!@*/false;function revlog_highlight(){var A=$("revlog_body");$A(A.getElementsByTagName("TR")).each(function(B){if(isMSIE){Event.observe(B,"mouseover",(function(){Element.addClassName(this,"hover")}).bind(B));Event.observe(B,"mouseout",(function(){Element.removeClassName(this,"hover")}).bind(B))}Event.observe(B,"click",revlog_toggle.bindAsEventListener(B))})}function revlog_toggle(B){var A=Event.element(B);while(A!=this){if(A.tagName.toUpperCase()=="A"&&A.getAttribute("href")){return }A=A.parentNode}if(revlog_selected!=null){Element.removeClassName(revlog_selected,"selected");if(revlog_selected==this){revlog_selected=null;Element.removeClassName("revlog_body","selection");return }}revlog_selected=this;Element.addClassName(this,"selected");Element.addClassName("revlog_body","selection")}function revlog_sdiff(A){A.href=A.href.replace(/r1=([\d\.]+)/,"r1="+revlog_selected.id.substring(3))}Event.observe(window,"load",revlog_highlight)
\ No newline at end of file
diff --git a/js/src/QuickFinder.js b/js/src/QuickFinder.js
new file mode 100644
index 0000000..660f841
--- /dev/null
+++ b/js/src/QuickFinder.js
@@ -0,0 +1,101 @@
+/**
+ * Component for filtering a table or any list of children based on
+ * the dynamic value of a text input. It requires the prototype.js
+ * library.
+ *
+ * You should define the CSS class .QuickFinderNoMatch to say what
+ * happens to items that don't match the criteria. A reasonable
+ * default would be display:none.
+ *
+ * This code is heavily inspired by Filterlicious by Gavin
+ * Kistner. The filterlicious JavaScript file did not have a license;
+ * however, most of Gavin's code is under the license defined by
+ * http://phrogz.net/JS/_ReuseLicense.txt, so I'm including that URL
+ * and Gavin's name as acknowledgements.
+ *
+ * @author Chuck Hagenbuch <chuck at horde.org>
+ *
+ * $Horde: chora/js/src/QuickFinder.js,v 1.4.2.1 2008/10/09 17:40:05 jan Exp $
+ */
+
+var QuickFinder = {
+
+    attachBehavior: function() {
+        $$('input').each(function(input) {
+            var filterTarget = input.readAttribute('for');
+            if (!filterTarget) {
+                return;
+            }
+
+            if (filterTarget.indexOf(',') != -1) {
+                input.filterTargets = [];
+                var targets = filterTarget.split(',');
+                for (var i = 0; i < targets.length; ++i) {
+                    var t = $(targets[i]);
+                    if (t) {
+                        input.filterTargets.push(t);
+                    }
+                }
+                if (!input.filterTargets.length) {
+                    return;
+                }
+            } else {
+                input.filterTargets = [ $(filterTarget) ];
+                if (!input.filterTargets[0]) {
+                    return;
+                }
+            }
+
+            filterEmpty = input.readAttribute('empty');
+            if (filterEmpty) {
+                input.filterEmpty = $(filterEmpty);
+            }
+
+            input.observe('keyup', QuickFinder.onKeyUp);
+
+            for (var i = 0, i_max = input.filterTargets.length; i < i_max; i++) {
+                input.filterTargets[i].immediateDescendants().each(function(line) {
+                    var filterText = line.filterText || line.readAttribute('filterText');
+                    if (!filterText) {
+                        line.filterText = line.innerHTML.stripTags();
+                    }
+                    line.filterText = line.filterText.toLowerCase();
+                });
+            }
+
+            QuickFinder.filter(input);
+        });
+    },
+
+    onKeyUp: function(e) {
+        input = e.element();
+        if (input.filterTargets) {
+            QuickFinder.filter(input);
+        }
+    },
+
+    filter: function(input) {
+        var val = input.value.toLowerCase();
+        var matched = 0;
+        for (var i = 0, i_max = input.filterTargets.length; i < i_max; i++) {
+            input.filterTargets[i].immediateDescendants().each(function(line) {
+                var filterText = line.filterText;
+                if (filterText.indexOf(val) == -1) {
+                    line.addClassName('QuickFinderNoMatch');
+                } else {
+                    ++matched;
+                    line.removeClassName('QuickFinderNoMatch');
+                }
+            });
+        }
+
+        try {
+            if (input.filterEmpty) {
+                (matched == 0) ? input.filterEmpty.show() : input.filterEmpty.hide();
+            }
+        } catch (e) {}
+    }
+
+}
+
+document.observe('dom:loaded', QuickFinder.attachBehavior);
diff --git a/js/src/prototype.js b/js/src/prototype.js
new file mode 100644
index 0000000..a7fd383
--- /dev/null
+++ b/js/src/prototype.js
@@ -0,0 +1,4320 @@
+/*  Prototype JavaScript framework, version 1.6.0.3
+ *  (c) 2005-2008 Sam Stephenson
+ *
+ *  Prototype is freely distributable under the terms of an MIT-style license.
+ *  For details, see the Prototype web site: http://www.prototypejs.org/
+ *
+ *--------------------------------------------------------------------------*/
+
+var Prototype = {
+  Version: '1.6.0.3',
+
+  Browser: {
+    IE:     !!(window.attachEvent &&
+      navigator.userAgent.indexOf('Opera') === -1),
+    Opera:  navigator.userAgent.indexOf('Opera') > -1,
+    WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
+    Gecko:  navigator.userAgent.indexOf('Gecko') > -1 &&
+      navigator.userAgent.indexOf('KHTML') === -1,
+    MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)
+  },
+
+  BrowserFeatures: {
+    XPath: !!document.evaluate,
+    SelectorsAPI: !!document.querySelector,
+    ElementExtensions: !!window.HTMLElement,
+    SpecificElementExtensions:
+      document.createElement('div')['__proto__'] &&
+      document.createElement('div')['__proto__'] !==
+        document.createElement('form')['__proto__']
+  },
+
+  ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
+  JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
+
+  emptyFunction: function() { },
+  K: function(x) { return x }
+};
+
+if (Prototype.Browser.MobileSafari)
+  Prototype.BrowserFeatures.SpecificElementExtensions = false;
+
+
+/* Based on Alex Arnell's inheritance implementation. */
+var Class = {
+  create: function() {
+    var parent = null, properties = $A(arguments);
+    if (Object.isFunction(properties[0]))
+      parent = properties.shift();
+
+    function klass() {
+      this.initialize.apply(this, arguments);
+    }
+
+    Object.extend(klass, Class.Methods);
+    klass.superclass = parent;
+    klass.subclasses = [];
+
+    if (parent) {
+      var subclass = function() { };
+      subclass.prototype = parent.prototype;
+      klass.prototype = new subclass;
+      parent.subclasses.push(klass);
+    }
+
+    for (var i = 0; i < properties.length; i++)
+      klass.addMethods(properties[i]);
+
+    if (!klass.prototype.initialize)
+      klass.prototype.initialize = Prototype.emptyFunction;
+
+    klass.prototype.constructor = klass;
+
+    return klass;
+  }
+};
+
+Class.Methods = {
+  addMethods: function(source) {
+    var ancestor   = this.superclass && this.superclass.prototype;
+    var properties = Object.keys(source);
+
+    if (!Object.keys({ toString: true }).length)
+      properties.push("toString", "valueOf");
+
+    for (var i = 0, length = properties.length; i < length; i++) {
+      var property = properties[i], value = source[property];
+      if (ancestor && Object.isFunction(value) &&
+          value.argumentNames().first() == "$super") {
+        var method = value;
+        value = (function(m) {
+          return function() { return ancestor[m].apply(this, arguments) };
+        })(property).wrap(method);
+
+        value.valueOf = method.valueOf.bind(method);
+        value.toString = method.toString.bind(method);
+      }
+      this.prototype[property] = value;
+    }
+
+    return this;
+  }
+};
+
+var Abstract = { };
+
+Object.extend = function(destination, source) {
+  for (var property in source)
+    destination[property] = source[property];
+  return destination;
+};
+
+Object.extend(Object, {
+  inspect: function(object) {
+    try {
+      if (Object.isUndefined(object)) return 'undefined';
+      if (object === null) return 'null';
+      return object.inspect ? object.inspect() : String(object);
+    } catch (e) {
+      if (e instanceof RangeError) return '...';
+      throw e;
+    }
+  },
+
+  toJSON: function(object) {
+    var type = typeof object;
+    switch (type) {
+      case 'undefined':
+      case 'function':
+      case 'unknown': return;
+      case 'boolean': return object.toString();
+    }
+
+    if (object === null) return 'null';
+    if (object.toJSON) return object.toJSON();
+    if (Object.isElement(object)) return;
+
+    var results = [];
+    for (var property in object) {
+      var value = Object.toJSON(object[property]);
+      if (!Object.isUndefined(value))
+        results.push(property.toJSON() + ': ' + value);
+    }
+
+    return '{' + results.join(', ') + '}';
+  },
+
+  toQueryString: function(object) {
+    return $H(object).toQueryString();
+  },
+
+  toHTML: function(object) {
+    return object && object.toHTML ? object.toHTML() : String.interpret(object);
+  },
+
+  keys: function(object) {
+    var keys = [];
+    for (var property in object)
+      keys.push(property);
+    return keys;
+  },
+
+  values: function(object) {
+    var values = [];
+    for (var property in object)
+      values.push(object[property]);
+    return values;
+  },
+
+  clone: function(object) {
+    return Object.extend({ }, object);
+  },
+
+  isElement: function(object) {
+    return !!(object && object.nodeType == 1);
+  },
+
+  isArray: function(object) {
+    return object != null && typeof object == "object" &&
+      'splice' in object && 'join' in object;
+  },
+
+  isHash: function(object) {
+    return object instanceof Hash;
+  },
+
+  isFunction: function(object) {
+    return typeof object == "function";
+  },
+
+  isString: function(object) {
+    return typeof object == "string";
+  },
+
+  isNumber: function(object) {
+    return typeof object == "number";
+  },
+
+  isUndefined: function(object) {
+    return typeof object == "undefined";
+  }
+});
+
+Object.extend(Function.prototype, {
+  argumentNames: function() {
+    var names = this.toString().match(/^[\s\(]*function[^(]*\(([^\)]*)\)/)[1]
+      .replace(/\s+/g, '').split(',');
+    return names.length == 1 && !names[0] ? [] : names;
+  },
+
+  bind: function() {
+    if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
+    var __method = this, args = $A(arguments), object = args.shift();
+    return function() {
+      return __method.apply(object, args.concat($A(arguments)));
+    }
+  },
+
+  bindAsEventListener: function() {
+    var __method = this, args = $A(arguments), object = args.shift();
+    return function(event) {
+      return __method.apply(object, [event || window.event].concat(args));
+    }
+  },
+
+  curry: function() {
+    if (!arguments.length) return this;
+    var __method = this, args = $A(arguments);
+    return function() {
+      return __method.apply(this, args.concat($A(arguments)));
+    }
+  },
+
+  delay: function() {
+    var __method = this, args = $A(arguments), timeout = args.shift() * 1000;
+    return window.setTimeout(function() {
+      return __method.apply(__method, args);
+    }, timeout);
+  },
+
+  defer: function() {
+    var args = [0.01].concat($A(arguments));
+    return this.delay.apply(this, args);
+  },
+
+  wrap: function(wrapper) {
+    var __method = this;
+    return function() {
+      return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));
+    }
+  },
+
+  methodize: function() {
+    if (this._methodized) return this._methodized;
+    var __method = this;
+    return this._methodized = function() {
+      return __method.apply(null, [this].concat($A(arguments)));
+    };
+  }
+});
+
+Date.prototype.toJSON = function() {
+  return '"' + this.getUTCFullYear() + '-' +
+    (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
+    this.getUTCDate().toPaddedString(2) + 'T' +
+    this.getUTCHours().toPaddedString(2) + ':' +
+    this.getUTCMinutes().toPaddedString(2) + ':' +
+    this.getUTCSeconds().toPaddedString(2) + 'Z"';
+};
+
+var Try = {
+  these: function() {
+    var returnValue;
+
+    for (var i = 0, length = arguments.length; i < length; i++) {
+      var lambda = arguments[i];
+      try {
+        returnValue = lambda();
+        break;
+      } catch (e) { }
+    }
+
+    return returnValue;
+  }
+};
+
+RegExp.prototype.match = RegExp.prototype.test;
+
+RegExp.escape = function(str) {
+  return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
+};
+
+/*--------------------------------------------------------------------------*/
+
+var PeriodicalExecuter = Class.create({
+  initialize: function(callback, frequency) {
+    this.callback = callback;
+    this.frequency = frequency;
+    this.currentlyExecuting = false;
+
+    this.registerCallback();
+  },
+
+  registerCallback: function() {
+    this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+  },
+
+  execute: function() {
+    this.callback(this);
+  },
+
+  stop: function() {
+    if (!this.timer) return;
+    clearInterval(this.timer);
+    this.timer = null;
+  },
+
+  onTimerEvent: function() {
+    if (!this.currentlyExecuting) {
+      try {
+        this.currentlyExecuting = true;
+        this.execute();
+      } finally {
+        this.currentlyExecuting = false;
+      }
+    }
+  }
+});
+Object.extend(String, {
+  interpret: function(value) {
+    return value == null ? '' : String(value);
+  },
+  specialChar: {
+    '\b': '\\b',
+    '\t': '\\t',
+    '\n': '\\n',
+    '\f': '\\f',
+    '\r': '\\r',
+    '\\': '\\\\'
+  }
+});
+
+Object.extend(String.prototype, {
+  gsub: function(pattern, replacement) {
+    var result = '', source = this, match;
+    replacement = arguments.callee.prepareReplacement(replacement);
+
+    while (source.length > 0) {
+      if (match = source.match(pattern)) {
+        result += source.slice(0, match.index);
+        result += String.interpret(replacement(match));
+        source  = source.slice(match.index + match[0].length);
+      } else {
+        result += source, source = '';
+      }
+    }
+    return result;
+  },
+
+  sub: function(pattern, replacement, count) {
+    replacement = this.gsub.prepareReplacement(replacement);
+    count = Object.isUndefined(count) ? 1 : count;
+
+    return this.gsub(pattern, function(match) {
+      if (--count < 0) return match[0];
+      return replacement(match);
+    });
+  },
+
+  scan: function(pattern, iterator) {
+    this.gsub(pattern, iterator);
+    return String(this);
+  },
+
+  truncate: function(length, truncation) {
+    length = length || 30;
+    truncation = Object.isUndefined(truncation) ? '...' : truncation;
+    return this.length > length ?
+      this.slice(0, length - truncation.length) + truncation : String(this);
+  },
+
+  strip: function() {
+    return this.replace(/^\s+/, '').replace(/\s+$/, '');
+  },
+
+  stripTags: function() {
+    return this.replace(/<\/?[^>]+>/gi, '');
+  },
+
+  stripScripts: function() {
+    return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
+  },
+
+  extractScripts: function() {
+    var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
+    var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
+    return (this.match(matchAll) || []).map(function(scriptTag) {
+      return (scriptTag.match(matchOne) || ['', ''])[1];
+    });
+  },
+
+  evalScripts: function() {
+    return this.extractScripts().map(function(script) { return eval(script) });
+  },
+
+  escapeHTML: function() {
+    var self = arguments.callee;
+    self.text.data = this;
+    return self.div.innerHTML;
+  },
+
+  unescapeHTML: function() {
+    var div = new Element('div');
+    div.innerHTML = this.stripTags();
+    return div.childNodes[0] ? (div.childNodes.length > 1 ?
+      $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
+      div.childNodes[0].nodeValue) : '';
+  },
+
+  toQueryParams: function(separator) {
+    var match = this.strip().match(/([^?#]*)(#.*)?$/);
+    if (!match) return { };
+
+    return match[1].split(separator || '&').inject({ }, function(hash, pair) {
+      if ((pair = pair.split('='))[0]) {
+        var key = decodeURIComponent(pair.shift());
+        var value = pair.length > 1 ? pair.join('=') : pair[0];
+        if (value != undefined) value = decodeURIComponent(value);
+
+        if (key in hash) {
+          if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
+          hash[key].push(value);
+        }
+        else hash[key] = value;
+      }
+      return hash;
+    });
+  },
+
+  toArray: function() {
+    return this.split('');
+  },
+
+  succ: function() {
+    return this.slice(0, this.length - 1) +
+      String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
+  },
+
+  times: function(count) {
+    return count < 1 ? '' : new Array(count + 1).join(this);
+  },
+
+  camelize: function() {
+    var parts = this.split('-'), len = parts.length;
+    if (len == 1) return parts[0];
+
+    var camelized = this.charAt(0) == '-'
+      ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
+      : parts[0];
+
+    for (var i = 1; i < len; i++)
+      camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
+
+    return camelized;
+  },
+
+  capitalize: function() {
+    return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
+  },
+
+  underscore: function() {
+    return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
+  },
+
+  dasherize: function() {
+    return this.gsub(/_/,'-');
+  },
+
+  inspect: function(useDoubleQuotes) {
+    var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
+      var character = String.specialChar[match[0]];
+      return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
+    });
+    if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
+    return "'" + escapedString.replace(/'/g, '\\\'') + "'";
+  },
+
+  toJSON: function() {
+    return this.inspect(true);
+  },
+
+  unfilterJSON: function(filter) {
+    return this.sub(filter || Prototype.JSONFilter, '#{1}');
+  },
+
+  isJSON: function() {
+    var str = this;
+    if (str.blank()) return false;
+    str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
+    return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
+  },
+
+  evalJSON: function(sanitize) {
+    var json = this.unfilterJSON();
+    try {
+      if (!sanitize || json.isJSON()) return eval('(' + json + ')');
+    } catch (e) { }
+    throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
+  },
+
+  include: function(pattern) {
+    return this.indexOf(pattern) > -1;
+  },
+
+  startsWith: function(pattern) {
+    return this.indexOf(pattern) === 0;
+  },
+
+  endsWith: function(pattern) {
+    var d = this.length - pattern.length;
+    return d >= 0 && this.lastIndexOf(pattern) === d;
+  },
+
+  empty: function() {
+    return this == '';
+  },
+
+  blank: function() {
+    return /^\s*$/.test(this);
+  },
+
+  interpolate: function(object, pattern) {
+    return new Template(this, pattern).evaluate(object);
+  }
+});
+
+if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
+  escapeHTML: function() {
+    return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
+  },
+  unescapeHTML: function() {
+    return this.stripTags().replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
+  }
+});
+
+String.prototype.gsub.prepareReplacement = function(replacement) {
+  if (Object.isFunction(replacement)) return replacement;
+  var template = new Template(replacement);
+  return function(match) { return template.evaluate(match) };
+};
+
+String.prototype.parseQuery = String.prototype.toQueryParams;
+
+Object.extend(String.prototype.escapeHTML, {
+  div:  document.createElement('div'),
+  text: document.createTextNode('')
+});
+
+String.prototype.escapeHTML.div.appendChild(String.prototype.escapeHTML.text);
+
+var Template = Class.create({
+  initialize: function(template, pattern) {
+    this.template = template.toString();
+    this.pattern = pattern || Template.Pattern;
+  },
+
+  evaluate: function(object) {
+    if (Object.isFunction(object.toTemplateReplacements))
+      object = object.toTemplateReplacements();
+
+    return this.template.gsub(this.pattern, function(match) {
+      if (object == null) return '';
+
+      var before = match[1] || '';
+      if (before == '\\') return match[2];
+
+      var ctx = object, expr = match[3];
+      var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
+      match = pattern.exec(expr);
+      if (match == null) return before;
+
+      while (match != null) {
+        var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1];
+        ctx = ctx[comp];
+        if (null == ctx || '' == match[3]) break;
+        expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
+        match = pattern.exec(expr);
+      }
+
+      return before + String.interpret(ctx);
+    });
+  }
+});
+Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
+
+var $break = { };
+
+var Enumerable = {
+  each: function(iterator, context) {
+    var index = 0;
+    try {
+      this._each(function(value) {
+        iterator.call(context, value, index++);
+      });
+    } catch (e) {
+      if (e != $break) throw e;
+    }
+    return this;
+  },
+
+  eachSlice: function(number, iterator, context) {
+    var index = -number, slices = [], array = this.toArray();
+    if (number < 1) return array;
+    while ((index += number) < array.length)
+      slices.push(array.slice(index, index+number));
+    return slices.collect(iterator, context);
+  },
+
+  all: function(iterator, context) {
+    iterator = iterator || Prototype.K;
+    var result = true;
+    this.each(function(value, index) {
+      result = result && !!iterator.call(context, value, index);
+      if (!result) throw $break;
+    });
+    return result;
+  },
+
+  any: function(iterator, context) {
+    iterator = iterator || Prototype.K;
+    var result = false;
+    this.each(function(value, index) {
+      if (result = !!iterator.call(context, value, index))
+        throw $break;
+    });
+    return result;
+  },
+
+  collect: function(iterator, context) {
+    iterator = iterator || Prototype.K;
+    var results = [];
+    this.each(function(value, index) {
+      results.push(iterator.call(context, value, index));
+    });
+    return results;
+  },
+
+  detect: function(iterator, context) {
+    var result;
+    this.each(function(value, index) {
+      if (iterator.call(context, value, index)) {
+        result = value;
+        throw $break;
+      }
+    });
+    return result;
+  },
+
+  findAll: function(iterator, context) {
+    var results = [];
+    this.each(function(value, index) {
+      if (iterator.call(context, value, index))
+        results.push(value);
+    });
+    return results;
+  },
+
+  grep: function(filter, iterator, context) {
+    iterator = iterator || Prototype.K;
+    var results = [];
+
+    if (Object.isString(filter))
+      filter = new RegExp(filter);
+
+    this.each(function(value, index) {
+      if (filter.match(value))
+        results.push(iterator.call(context, value, index));
+    });
+    return results;
+  },
+
+  include: function(object) {
+    if (Object.isFunction(this.indexOf))
+      if (this.indexOf(object) != -1) return true;
+
+    var found = false;
+    this.each(function(value) {
+      if (value == object) {
+        found = true;
+        throw $break;
+      }
+    });
+    return found;
+  },
+
+  inGroupsOf: function(number, fillWith) {
+    fillWith = Object.isUndefined(fillWith) ? null : fillWith;
+    return this.eachSlice(number, function(slice) {
+      while(slice.length < number) slice.push(fillWith);
+      return slice;
+    });
+  },
+
+  inject: function(memo, iterator, context) {
+    this.each(function(value, index) {
+      memo = iterator.call(context, memo, value, index);
+    });
+    return memo;
+  },
+
+  invoke: function(method) {
+    var args = $A(arguments).slice(1);
+    return this.map(function(value) {
+      return value[method].apply(value, args);
+    });
+  },
+
+  max: function(iterator, context) {
+    iterator = iterator || Prototype.K;
+    var result;
+    this.each(function(value, index) {
+      value = iterator.call(context, value, index);
+      if (result == null || value >= result)
+        result = value;
+    });
+    return result;
+  },
+
+  min: function(iterator, context) {
+    iterator = iterator || Prototype.K;
+    var result;
+    this.each(function(value, index) {
+      value = iterator.call(context, value, index);
+      if (result == null || value < result)
+        result = value;
+    });
+    return result;
+  },
+
+  partition: function(iterator, context) {
+    iterator = iterator || Prototype.K;
+    var trues = [], falses = [];
+    this.each(function(value, index) {
+      (iterator.call(context, value, index) ?
+        trues : falses).push(value);
+    });
+    return [trues, falses];
+  },
+
+  pluck: function(property) {
+    var results = [];
+    this.each(function(value) {
+      results.push(value[property]);
+    });
+    return results;
+  },
+
+  reject: function(iterator, context) {
+    var results = [];
+    this.each(function(value, index) {
+      if (!iterator.call(context, value, index))
+        results.push(value);
+    });
+    return results;
+  },
+
+  sortBy: function(iterator, context) {
+    return this.map(function(value, index) {
+      return {
+        value: value,
+        criteria: iterator.call(context, value, index)
+      };
+    }).sort(function(left, right) {
+      var a = left.criteria, b = right.criteria;
+      return a < b ? -1 : a > b ? 1 : 0;
+    }).pluck('value');
+  },
+
+  toArray: function() {
+    return this.map();
+  },
+
+  zip: function() {
+    var iterator = Prototype.K, args = $A(arguments);
+    if (Object.isFunction(args.last()))
+      iterator = args.pop();
+
+    var collections = [this].concat(args).map($A);
+    return this.map(function(value, index) {
+      return iterator(collections.pluck(index));
+    });
+  },
+
+  size: function() {
+    return this.toArray().length;
+  },
+
+  inspect: function() {
+    return '#<Enumerable:' + this.toArray().inspect() + '>';
+  }
+};
+
+Object.extend(Enumerable, {
+  map:     Enumerable.collect,
+  find:    Enumerable.detect,
+  select:  Enumerable.findAll,
+  filter:  Enumerable.findAll,
+  member:  Enumerable.include,
+  entries: Enumerable.toArray,
+  every:   Enumerable.all,
+  some:    Enumerable.any
+});
+function $A(iterable) {
+  if (!iterable) return [];
+  if (iterable.toArray) return iterable.toArray();
+  var length = iterable.length || 0, results = new Array(length);
+  while (length--) results[length] = iterable[length];
+  return results;
+}
+
+if (Prototype.Browser.WebKit) {
+  $A = function(iterable) {
+    if (!iterable) return [];
+    // In Safari, only use the `toArray` method if it's not a NodeList.
+    // A NodeList is a function, has an function `item` property, and a numeric
+    // `length` property. Adapted from Google Doctype.
+    if (!(typeof iterable === 'function' && typeof iterable.length ===
+        'number' && typeof iterable.item === 'function') && iterable.toArray)
+      return iterable.toArray();
+    var length = iterable.length || 0, results = new Array(length);
+    while (length--) results[length] = iterable[length];
+    return results;
+  };
+}
+
+Array.from = $A;
+
+Object.extend(Array.prototype, Enumerable);
+
+if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse;
+
+Object.extend(Array.prototype, {
+  _each: function(iterator) {
+    for (var i = 0, length = this.length; i < length; i++)
+      iterator(this[i]);
+  },
+
+  clear: function() {
+    this.length = 0;
+    return this;
+  },
+
+  first: function() {
+    return this[0];
+  },
+
+  last: function() {
+    return this[this.length - 1];
+  },
+
+  compact: function() {
+    return this.select(function(value) {
+      return value != null;
+    });
+  },
+
+  flatten: function() {
+    return this.inject([], function(array, value) {
+      return array.concat(Object.isArray(value) ?
+        value.flatten() : [value]);
+    });
+  },
+
+  without: function() {
+    var values = $A(arguments);
+    return this.select(function(value) {
+      return !values.include(value);
+    });
+  },
+
+  reverse: function(inline) {
+    return (inline !== false ? this : this.toArray())._reverse();
+  },
+
+  reduce: function() {
+    return this.length > 1 ? this : this[0];
+  },
+
+  uniq: function(sorted) {
+    return this.inject([], function(array, value, index) {
+      if (0 == index || (sorted ? array.last() != value : !array.include(value)))
+        array.push(value);
+      return array;
+    });
+  },
+
+  intersect: function(array) {
+    return this.uniq().findAll(function(item) {
+      return array.detect(function(value) { return item === value });
+    });
+  },
+
+  clone: function() {
+    return [].concat(this);
+  },
+
+  size: function() {
+    return this.length;
+  },
+
+  inspect: function() {
+    return '[' + this.map(Object.inspect).join(', ') + ']';
+  },
+
+  toJSON: function() {
+    var results = [];
+    this.each(function(object) {
+      var value = Object.toJSON(object);
+      if (!Object.isUndefined(value)) results.push(value);
+    });
+    return '[' + results.join(', ') + ']';
+  }
+});
+
+// use native browser JS 1.6 implementation if available
+if (Object.isFunction(Array.prototype.forEach))
+  Array.prototype._each = Array.prototype.forEach;
+
+if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) {
+  i || (i = 0);
+  var length = this.length;
+  if (i < 0) i = length + i;
+  for (; i < length; i++)
+    if (this[i] === item) return i;
+  return -1;
+};
+
+if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) {
+  i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
+  var n = this.slice(0, i).reverse().indexOf(item);
+  return (n < 0) ? n : i - n - 1;
+};
+
+Array.prototype.toArray = Array.prototype.clone;
+
+function $w(string) {
+  if (!Object.isString(string)) return [];
+  string = string.strip();
+  return string ? string.split(/\s+/) : [];
+}
+
+if (Prototype.Browser.Opera){
+  Array.prototype.concat = function() {
+    var array = [];
+    for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
+    for (var i = 0, length = arguments.length; i < length; i++) {
+      if (Object.isArray(arguments[i])) {
+        for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
+          array.push(arguments[i][j]);
+      } else {
+        array.push(arguments[i]);
+      }
+    }
+    return array;
+  };
+}
+Object.extend(Number.prototype, {
+  toColorPart: function() {
+    return this.toPaddedString(2, 16);
+  },
+
+  succ: function() {
+    return this + 1;
+  },
+
+  times: function(iterator, context) {
+    $R(0, this, true).each(iterator, context);
+    return this;
+  },
+
+  toPaddedString: function(length, radix) {
+    var string = this.toString(radix || 10);
+    return '0'.times(length - string.length) + string;
+  },
+
+  toJSON: function() {
+    return isFinite(this) ? this.toString() : 'null';
+  }
+});
+
+$w('abs round ceil floor').each(function(method){
+  Number.prototype[method] = Math[method].methodize();
+});
+function $H(object) {
+  return new Hash(object);
+};
+
+var Hash = Class.create(Enumerable, (function() {
+
+  function toQueryPair(key, value) {
+    if (Object.isUndefined(value)) return key;
+    return key + '=' + encodeURIComponent(String.interpret(value));
+  }
+
+  return {
+    initialize: function(object) {
+      this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
+    },
+
+    _each: function(iterator) {
+      for (var key in this._object) {
+        var value = this._object[key], pair = [key, value];
+        pair.key = key;
+        pair.value = value;
+        iterator(pair);
+      }
+    },
+
+    set: function(key, value) {
+      return this._object[key] = value;
+    },
+
+    get: function(key) {
+      // simulating poorly supported hasOwnProperty
+      if (this._object[key] !== Object.prototype[key])
+        return this._object[key];
+    },
+
+    unset: function(key) {
+      var value = this._object[key];
+      delete this._object[key];
+      return value;
+    },
+
+    toObject: function() {
+      return Object.clone(this._object);
+    },
+
+    keys: function() {
+      return this.pluck('key');
+    },
+
+    values: function() {
+      return this.pluck('value');
+    },
+
+    index: function(value) {
+      var match = this.detect(function(pair) {
+        return pair.value === value;
+      });
+      return match && match.key;
+    },
+
+    merge: function(object) {
+      return this.clone().update(object);
+    },
+
+    update: function(object) {
+      return new Hash(object).inject(this, function(result, pair) {
+        result.set(pair.key, pair.value);
+        return result;
+      });
+    },
+
+    toQueryString: function() {
+      return this.inject([], function(results, pair) {
+        var key = encodeURIComponent(pair.key), values = pair.value;
+
+        if (values && typeof values == 'object') {
+          if (Object.isArray(values))
+            return results.concat(values.map(toQueryPair.curry(key)));
+        } else results.push(toQueryPair(key, values));
+        return results;
+      }).join('&');
+    },
+
+    inspect: function() {
+      return '#<Hash:{' + this.map(function(pair) {
+        return pair.map(Object.inspect).join(': ');
+      }).join(', ') + '}>';
+    },
+
+    toJSON: function() {
+      return Object.toJSON(this.toObject());
+    },
+
+    clone: function() {
+      return new Hash(this);
+    }
+  }
+})());
+
+Hash.prototype.toTemplateReplacements = Hash.prototype.toObject;
+Hash.from = $H;
+var ObjectRange = Class.create(Enumerable, {
+  initialize: function(start, end, exclusive) {
+    this.start = start;
+    this.end = end;
+    this.exclusive = exclusive;
+  },
+
+  _each: function(iterator) {
+    var value = this.start;
+    while (this.include(value)) {
+      iterator(value);
+      value = value.succ();
+    }
+  },
+
+  include: function(value) {
+    if (value < this.start)
+      return false;
+    if (this.exclusive)
+      return value < this.end;
+    return value <= this.end;
+  }
+});
+
+var $R = function(start, end, exclusive) {
+  return new ObjectRange(start, end, exclusive);
+};
+
+var Ajax = {
+  getTransport: function() {
+    return Try.these(
+      function() {return new XMLHttpRequest()},
+      function() {return new ActiveXObject('Msxml2.XMLHTTP')},
+      function() {return new ActiveXObject('Microsoft.XMLHTTP')}
+    ) || false;
+  },
+
+  activeRequestCount: 0
+};
+
+Ajax.Responders = {
+  responders: [],
+
+  _each: function(iterator) {
+    this.responders._each(iterator);
+  },
+
+  register: function(responder) {
+    if (!this.include(responder))
+      this.responders.push(responder);
+  },
+
+  unregister: function(responder) {
+    this.responders = this.responders.without(responder);
+  },
+
+  dispatch: function(callback, request, transport, json) {
+    this.each(function(responder) {
+      if (Object.isFunction(responder[callback])) {
+        try {
+          responder[callback].apply(responder, [request, transport, json]);
+        } catch (e) { }
+      }
+    });
+  }
+};
+
+Object.extend(Ajax.Responders, Enumerable);
+
+Ajax.Responders.register({
+  onCreate:   function() { Ajax.activeRequestCount++ },
+  onComplete: function() { Ajax.activeRequestCount-- }
+});
+
+Ajax.Base = Class.create({
+  initialize: function(options) {
+    this.options = {
+      method:       'post',
+      asynchronous: true,
+      contentType:  'application/x-www-form-urlencoded',
+      encoding:     'UTF-8',
+      parameters:   '',
+      evalJSON:     true,
+      evalJS:       true
+    };
+    Object.extend(this.options, options || { });
+
+    this.options.method = this.options.method.toLowerCase();
+
+    if (Object.isString(this.options.parameters))
+      this.options.parameters = this.options.parameters.toQueryParams();
+    else if (Object.isHash(this.options.parameters))
+      this.options.parameters = this.options.parameters.toObject();
+  }
+});
+
+Ajax.Request = Class.create(Ajax.Base, {
+  _complete: false,
+
+  initialize: function($super, url, options) {
+    $super(options);
+    this.transport = Ajax.getTransport();
+    this.request(url);
+  },
+
+  request: function(url) {
+    this.url = url;
+    this.method = this.options.method;
+    var params = Object.clone(this.options.parameters);
+
+    if (!['get', 'post'].include(this.method)) {
+      // simulate other verbs over post
+      params['_method'] = this.method;
+      this.method = 'post';
+    }
+
+    this.parameters = params;
+
+    if (params = Object.toQueryString(params)) {
+      // when GET, append parameters to URL
+      if (this.method == 'get')
+        this.url += (this.url.include('?') ? '&' : '?') + params;
+      else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
+        params += '&_=';
+    }
+
+    try {
+      var response = new Ajax.Response(this);
+      if (this.options.onCreate) this.options.onCreate(response);
+      Ajax.Responders.dispatch('onCreate', this, response);
+
+      this.transport.open(this.method.toUpperCase(), this.url,
+        this.options.asynchronous);
+
+      if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
+
+      this.transport.onreadystatechange = this.onStateChange.bind(this);
+      this.setRequestHeaders();
+
+      this.body = this.method == 'post' ? (this.options.postBody || params) : null;
+      this.transport.send(this.body);
+
+      /* Force Firefox to handle ready state 4 for synchronous requests */
+      if (!this.options.asynchronous && this.transport.overrideMimeType)
+        this.onStateChange();
+
+    }
+    catch (e) {
+      this.dispatchException(e);
+    }
+  },
+
+  onStateChange: function() {
+    var readyState = this.transport.readyState;
+    if (readyState > 1 && !((readyState == 4) && this._complete))
+      this.respondToReadyState(this.transport.readyState);
+  },
+
+  setRequestHeaders: function() {
+    var headers = {
+      'X-Requested-With': 'XMLHttpRequest',
+      'X-Prototype-Version': Prototype.Version,
+      'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
+    };
+
+    if (this.method == 'post') {
+      headers['Content-type'] = this.options.contentType +
+        (this.options.encoding ? '; charset=' + this.options.encoding : '');
+
+      /* Force "Connection: close" for older Mozilla browsers to work
+       * around a bug where XMLHttpRequest sends an incorrect
+       * Content-length header. See Mozilla Bugzilla #246651.
+       */
+      if (this.transport.overrideMimeType &&
+          (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
+            headers['Connection'] = 'close';
+    }
+
+    // user-defined headers
+    if (typeof this.options.requestHeaders == 'object') {
+      var extras = this.options.requestHeaders;
+
+      if (Object.isFunction(extras.push))
+        for (var i = 0, length = extras.length; i < length; i += 2)
+          headers[extras[i]] = extras[i+1];
+      else
+        $H(extras).each(function(pair) { headers[pair.key] = pair.value });
+    }
+
+    for (var name in headers)
+      this.transport.setRequestHeader(name, headers[name]);
+  },
+
+  success: function() {
+    var status = this.getStatus();
+    return !status || (status >= 200 && status < 300);
+  },
+
+  getStatus: function() {
+    try {
+      return this.transport.status || 0;
+    } catch (e) { return 0 }
+  },
+
+  respondToReadyState: function(readyState) {
+    var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);
+
+    if (state == 'Complete') {
+      try {
+        this._complete = true;
+        (this.options['on' + response.status]
+         || this.options['on' + (this.success() ? 'Success' : 'Failure')]
+         || Prototype.emptyFunction)(response, response.headerJSON);
+      } catch (e) {
+        this.dispatchException(e);
+      }
+
+      var contentType = response.getHeader('Content-type');
+      if (this.options.evalJS == 'force'
+          || (this.options.evalJS && this.isSameOrigin() && contentType
+          && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
+        this.evalResponse();
+    }
+
+    try {
+      (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
+      Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
+    } catch (e) {
+      this.dispatchException(e);
+    }
+
+    if (state == 'Complete') {
+      // avoid memory leak in MSIE: clean up
+      this.transport.onreadystatechange = Prototype.emptyFunction;
+    }
+  },
+
+  isSameOrigin: function() {
+    var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
+    return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
+      protocol: location.protocol,
+      domain: document.domain,
+      port: location.port ? ':' + location.port : ''
+    }));
+  },
+
+  getHeader: function(name) {
+    try {
+      return this.transport.getResponseHeader(name) || null;
+    } catch (e) { return null }
+  },
+
+  evalResponse: function() {
+    try {
+      return eval((this.transport.responseText || '').unfilterJSON());
+    } catch (e) {
+      this.dispatchException(e);
+    }
+  },
+
+  dispatchException: function(exception) {
+    (this.options.onException || Prototype.emptyFunction)(this, exception);
+    Ajax.Responders.dispatch('onException', this, exception);
+  }
+});
+
+Ajax.Request.Events =
+  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+
+Ajax.Response = Class.create({
+  initialize: function(request){
+    this.request = request;
+    var transport  = this.transport  = request.transport,
+        readyState = this.readyState = transport.readyState;
+
+    if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
+      this.status       = this.getStatus();
+      this.statusText   = this.getStatusText();
+      this.responseText = String.interpret(transport.responseText);
+      this.headerJSON   = this._getHeaderJSON();
+    }
+
+    if(readyState == 4) {
+      var xml = transport.responseXML;
+      this.responseXML  = Object.isUndefined(xml) ? null : xml;
+      this.responseJSON = this._getResponseJSON();
+    }
+  },
+
+  status:      0,
+  statusText: '',
+
+  getStatus: Ajax.Request.prototype.getStatus,
+
+  getStatusText: function() {
+    try {
+      return this.transport.statusText || '';
+    } catch (e) { return '' }
+  },
+
+  getHeader: Ajax.Request.prototype.getHeader,
+
+  getAllHeaders: function() {
+    try {
+      return this.getAllResponseHeaders();
+    } catch (e) { return null }
+  },
+
+  getResponseHeader: function(name) {
+    return this.transport.getResponseHeader(name);
+  },
+
+  getAllResponseHeaders: function() {
+    return this.transport.getAllResponseHeaders();
+  },
+
+  _getHeaderJSON: function() {
+    var json = this.getHeader('X-JSON');
+    if (!json) return null;
+    json = decodeURIComponent(escape(json));
+    try {
+      return json.evalJSON(this.request.options.sanitizeJSON ||
+        !this.request.isSameOrigin());
+    } catch (e) {
+      this.request.dispatchException(e);
+    }
+  },
+
+  _getResponseJSON: function() {
+    var options = this.request.options;
+    if (!options.evalJSON || (options.evalJSON != 'force' &&
+      !(this.getHeader('Content-type') || '').include('application/json')) ||
+        this.responseText.blank())
+          return null;
+    try {
+      return this.responseText.evalJSON(options.sanitizeJSON ||
+        !this.request.isSameOrigin());
+    } catch (e) {
+      this.request.dispatchException(e);
+    }
+  }
+});
+
+Ajax.Updater = Class.create(Ajax.Request, {
+  initialize: function($super, container, url, options) {
+    this.container = {
+      success: (container.success || container),
+      failure: (container.failure || (container.success ? null : container))
+    };
+
+    options = Object.clone(options);
+    var onComplete = options.onComplete;
+    options.onComplete = (function(response, json) {
+      this.updateContent(response.responseText);
+      if (Object.isFunction(onComplete)) onComplete(response, json);
+    }).bind(this);
+
+    $super(url, options);
+  },
+
+  updateContent: function(responseText) {
+    var receiver = this.container[this.success() ? 'success' : 'failure'],
+        options = this.options;
+
+    if (!options.evalScripts) responseText = responseText.stripScripts();
+
+    if (receiver = $(receiver)) {
+      if (options.insertion) {
+        if (Object.isString(options.insertion)) {
+          var insertion = { }; insertion[options.insertion] = responseText;
+          receiver.insert(insertion);
+        }
+        else options.insertion(receiver, responseText);
+      }
+      else receiver.update(responseText);
+    }
+  }
+});
+
+Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
+  initialize: function($super, container, url, options) {
+    $super(options);
+    this.onComplete = this.options.onComplete;
+
+    this.frequency = (this.options.frequency || 2);
+    this.decay = (this.options.decay || 1);
+
+    this.updater = { };
+    this.container = container;
+    this.url = url;
+
+    this.start();
+  },
+
+  start: function() {
+    this.options.onComplete = this.updateComplete.bind(this);
+    this.onTimerEvent();
+  },
+
+  stop: function() {
+    this.updater.options.onComplete = undefined;
+    clearTimeout(this.timer);
+    (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
+  },
+
+  updateComplete: function(response) {
+    if (this.options.decay) {
+      this.decay = (response.responseText == this.lastText ?
+        this.decay * this.options.decay : 1);
+
+      this.lastText = response.responseText;
+    }
+    this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
+  },
+
+  onTimerEvent: function() {
+    this.updater = new Ajax.Updater(this.container, this.url, this.options);
+  }
+});
+function $(element) {
+  if (arguments.length > 1) {
+    for (var i = 0, elements = [], length = arguments.length; i < length; i++)
+      elements.push($(arguments[i]));
+    return elements;
+  }
+  if (Object.isString(element))
+    element = document.getElementById(element);
+  return Element.extend(element);
+}
+
+if (Prototype.BrowserFeatures.XPath) {
+  document._getElementsByXPath = function(expression, parentElement) {
+    var results = [];
+    var query = document.evaluate(expression, $(parentElement) || document,
+      null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
+    for (var i = 0, length = query.snapshotLength; i < length; i++)
+      results.push(Element.extend(query.snapshotItem(i)));
+    return results;
+  };
+}
+
+/*--------------------------------------------------------------------------*/
+
+if (!window.Node) var Node = { };
+
+if (!Node.ELEMENT_NODE) {
+  // DOM level 2 ECMAScript Language Binding
+  Object.extend(Node, {
+    ELEMENT_NODE: 1,
+    ATTRIBUTE_NODE: 2,
+    TEXT_NODE: 3,
+    CDATA_SECTION_NODE: 4,
+    ENTITY_REFERENCE_NODE: 5,
+    ENTITY_NODE: 6,
+    PROCESSING_INSTRUCTION_NODE: 7,
+    COMMENT_NODE: 8,
+    DOCUMENT_NODE: 9,
+    DOCUMENT_TYPE_NODE: 10,
+    DOCUMENT_FRAGMENT_NODE: 11,
+    NOTATION_NODE: 12
+  });
+}
+
+(function() {
+  var element = this.Element;
+  this.Element = function(tagName, attributes) {
+    attributes = attributes || { };
+    tagName = tagName.toLowerCase();
+    var cache = Element.cache;
+    if (Prototype.Browser.IE && attributes.name) {
+      tagName = '<' + tagName + ' name="' + attributes.name + '">';
+      delete attributes.name;
+      return Element.writeAttribute(document.createElement(tagName), attributes);
+    }
+    if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
+    return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);
+  };
+  Object.extend(this.Element, element || { });
+  if (element) this.Element.prototype = element.prototype;
+}).call(window);
+
+Element.cache = { };
+
+Element.Methods = {
+  visible: function(element) {
+    return $(element).style.display != 'none';
+  },
+
+  toggle: function(element) {
+    element = $(element);
+    Element[Element.visible(element) ? 'hide' : 'show'](element);
+    return element;
+  },
+
+  hide: function(element) {
+    element = $(element);
+    element.style.display = 'none';
+    return element;
+  },
+
+  show: function(element) {
+    element = $(element);
+    element.style.display = '';
+    return element;
+  },
+
+  remove: function(element) {
+    element = $(element);
+    element.parentNode.removeChild(element);
+    return element;
+  },
+
+  update: function(element, content) {
+    element = $(element);
+    if (content && content.toElement) content = content.toElement();
+    if (Object.isElement(content)) return element.update().insert(content);
+    content = Object.toHTML(content);
+    element.innerHTML = content.stripScripts();
+    content.evalScripts.bind(content).defer();
+    return element;
+  },
+
+  replace: function(element, content) {
+    element = $(element);
+    if (content && content.toElement) content = content.toElement();
+    else if (!Object.isElement(content)) {
+      content = Object.toHTML(content);
+      var range = element.ownerDocument.createRange();
+      range.selectNode(element);
+      content.evalScripts.bind(content).defer();
+      content = range.createContextualFragment(content.stripScripts());
+    }
+    element.parentNode.replaceChild(content, element);
+    return element;
+  },
+
+  insert: function(element, insertions) {
+    element = $(element);
+
+    if (Object.isString(insertions) || Object.isNumber(insertions) ||
+        Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
+          insertions = {bottom:insertions};
+
+    var content, insert, tagName, childNodes;
+
+    for (var position in insertions) {
+      content  = insertions[position];
+      position = position.toLowerCase();
+      insert = Element._insertionTranslations[position];
+
+      if (content && content.toElement) content = content.toElement();
+      if (Object.isElement(content)) {
+        insert(element, content);
+        continue;
+      }
+
+      content = Object.toHTML(content);
+
+      tagName = ((position == 'before' || position == 'after')
+        ? element.parentNode : element).tagName.toUpperCase();
+
+      childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
+
+      if (position == 'top' || position == 'after') childNodes.reverse();
+      childNodes.each(insert.curry(element));
+
+      content.evalScripts.bind(content).defer();
+    }
+
+    return element;
+  },
+
+  wrap: function(element, wrapper, attributes) {
+    element = $(element);
+    if (Object.isElement(wrapper))
+      $(wrapper).writeAttribute(attributes || { });
+    else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);
+    else wrapper = new Element('div', wrapper);
+    if (element.parentNode)
+      element.parentNode.replaceChild(wrapper, element);
+    wrapper.appendChild(element);
+    return wrapper;
+  },
+
+  inspect: function(element) {
+    element = $(element);
+    var result = '<' + element.tagName.toLowerCase();
+    $H({'id': 'id', 'className': 'class'}).each(function(pair) {
+      var property = pair.first(), attribute = pair.last();
+      var value = (element[property] || '').toString();
+      if (value) result += ' ' + attribute + '=' + value.inspect(true);
+    });
+    return result + '>';
+  },
+
+  recursivelyCollect: function(element, property) {
+    element = $(element);
+    var elements = [];
+    while (element = element[property])
+      if (element.nodeType == 1)
+        elements.push(Element.extend(element));
+    return elements;
+  },
+
+  ancestors: function(element) {
+    return $(element).recursivelyCollect('parentNode');
+  },
+
+  descendants: function(element) {
+    return $(element).select("*");
+  },
+
+  firstDescendant: function(element) {
+    element = $(element).firstChild;
+    while (element && element.nodeType != 1) element = element.nextSibling;
+    return $(element);
+  },
+
+  immediateDescendants: function(element) {
+    if (!(element = $(element).firstChild)) return [];
+    while (element && element.nodeType != 1) element = element.nextSibling;
+    if (element) return [element].concat($(element).nextSiblings());
+    return [];
+  },
+
+  previousSiblings: function(element) {
+    return $(element).recursivelyCollect('previousSibling');
+  },
+
+  nextSiblings: function(element) {
+    return $(element).recursivelyCollect('nextSibling');
+  },
+
+  siblings: function(element) {
+    element = $(element);
+    return element.previousSiblings().reverse().concat(element.nextSiblings());
+  },
+
+  match: function(element, selector) {
+    if (Object.isString(selector))
+      selector = new Selector(selector);
+    return selector.match($(element));
+  },
+
+  up: function(element, expression, index) {
+    element = $(element);
+    if (arguments.length == 1) return $(element.parentNode);
+    var ancestors = element.ancestors();
+    return Object.isNumber(expression) ? ancestors[expression] :
+      Selector.findElement(ancestors, expression, index);
+  },
+
+  down: function(element, expression, index) {
+    element = $(element);
+    if (arguments.length == 1) return element.firstDescendant();
+    return Object.isNumber(expression) ? element.descendants()[expression] :
+      Element.select(element, expression)[index || 0];
+  },
+
+  previous: function(element, expression, index) {
+    element = $(element);
+    if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
+    var previousSiblings = element.previousSiblings();
+    return Object.isNumber(expression) ? previousSiblings[expression] :
+      Selector.findElement(previousSiblings, expression, index);
+  },
+
+  next: function(element, expression, index) {
+    element = $(element);
+    if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
+    var nextSiblings = element.nextSiblings();
+    return Object.isNumber(expression) ? nextSiblings[expression] :
+      Selector.findElement(nextSiblings, expression, index);
+  },
+
+  select: function() {
+    var args = $A(arguments), element = $(args.shift());
+    return Selector.findChildElements(element, args);
+  },
+
+  adjacent: function() {
+    var args = $A(arguments), element = $(args.shift());
+    return Selector.findChildElements(element.parentNode, args).without(element);
+  },
+
+  identify: function(element) {
+    element = $(element);
+    var id = element.readAttribute('id'), self = arguments.callee;
+    if (id) return id;
+    do { id = 'anonymous_element_' + self.counter++ } while ($(id));
+    element.writeAttribute('id', id);
+    return id;
+  },
+
+  readAttribute: function(element, name) {
+    element = $(element);
+    if (Prototype.Browser.IE) {
+      var t = Element._attributeTranslations.read;
+      if (t.values[name]) return t.values[name](element, name);
+      if (t.names[name]) name = t.names[name];
+      if (name.include(':')) {
+        return (!element.attributes || !element.attributes[name]) ? null :
+         element.attributes[name].value;
+      }
+    }
+    return element.getAttribute(name);
+  },
+
+  writeAttribute: function(element, name, value) {
+    element = $(element);
+    var attributes = { }, t = Element._attributeTranslations.write;
+
+    if (typeof name == 'object') attributes = name;
+    else attributes[name] = Object.isUndefined(value) ? true : value;
+
+    for (var attr in attributes) {
+      name = t.names[attr] || attr;
+      value = attributes[attr];
+      if (t.values[attr]) name = t.values[attr](element, value);
+      if (value === false || value === null)
+        element.removeAttribute(name);
+      else if (value === true)
+        element.setAttribute(name, name);
+      else element.setAttribute(name, value);
+    }
+    return element;
+  },
+
+  getHeight: function(element) {
+    return $(element).getDimensions().height;
+  },
+
+  getWidth: function(element) {
+    return $(element).getDimensions().width;
+  },
+
+  classNames: function(element) {
+    return new Element.ClassNames(element);
+  },
+
+  hasClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    var elementClassName = element.className;
+    return (elementClassName.length > 0 && (elementClassName == className ||
+      new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
+  },
+
+  addClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    if (!element.hasClassName(className))
+      element.className += (element.className ? ' ' : '') + className;
+    return element;
+  },
+
+  removeClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    element.className = element.className.replace(
+      new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
+    return element;
+  },
+
+  toggleClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    return element[element.hasClassName(className) ?
+      'removeClassName' : 'addClassName'](className);
+  },
+
+  // removes whitespace-only text node children
+  cleanWhitespace: function(element) {
+    element = $(element);
+    var node = element.firstChild;
+    while (node) {
+      var nextNode = node.nextSibling;
+      if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
+        element.removeChild(node);
+      node = nextNode;
+    }
+    return element;
+  },
+
+  empty: function(element) {
+    return $(element).innerHTML.blank();
+  },
+
+  descendantOf: function(element, ancestor) {
+    element = $(element), ancestor = $(ancestor);
+
+    if (element.compareDocumentPosition)
+      return (element.compareDocumentPosition(ancestor) & 8) === 8;
+
+    if (ancestor.contains)
+      return ancestor.contains(element) && ancestor !== element;
+
+    while (element = element.parentNode)
+      if (element == ancestor) return true;
+
+    return false;
+  },
+
+  scrollTo: function(element) {
+    element = $(element);
+    var pos = element.cumulativeOffset();
+    window.scrollTo(pos[0], pos[1]);
+    return element;
+  },
+
+  getStyle: function(element, style) {
+    element = $(element);
+    style = style == 'float' ? 'cssFloat' : style.camelize();
+    var value = element.style[style];
+    if (!value || value == 'auto') {
+      var css = document.defaultView.getComputedStyle(element, null);
+      value = css ? css[style] : null;
+    }
+    if (style == 'opacity') return value ? parseFloat(value) : 1.0;
+    return value == 'auto' ? null : value;
+  },
+
+  getOpacity: function(element) {
+    return $(element).getStyle('opacity');
+  },
+
+  setStyle: function(element, styles) {
+    element = $(element);
+    var elementStyle = element.style, match;
+    if (Object.isString(styles)) {
+      element.style.cssText += ';' + styles;
+      return styles.include('opacity') ?
+        element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
+    }
+    for (var property in styles)
+      if (property == 'opacity') element.setOpacity(styles[property]);
+      else
+        elementStyle[(property == 'float' || property == 'cssFloat') ?
+          (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :
+            property] = styles[property];
+
+    return element;
+  },
+
+  setOpacity: function(element, value) {
+    element = $(element);
+    element.style.opacity = (value == 1 || value === '') ? '' :
+      (value < 0.00001) ? 0 : value;
+    return element;
+  },
+
+  getDimensions: function(element) {
+    element = $(element);
+    var display = element.getStyle('display');
+    if (display != 'none' && display != null) // Safari bug
+      return {width: element.offsetWidth, height: element.offsetHeight};
+
+    // All *Width and *Height properties give 0 on elements with display none,
+    // so enable the element temporarily
+    var els = element.style;
+    var originalVisibility = els.visibility;
+    var originalPosition = els.position;
+    var originalDisplay = els.display;
+    els.visibility = 'hidden';
+    els.position = 'absolute';
+    els.display = 'block';
+    var originalWidth = element.clientWidth;
+    var originalHeight = element.clientHeight;
+    els.display = originalDisplay;
+    els.position = originalPosition;
+    els.visibility = originalVisibility;
+    return {width: originalWidth, height: originalHeight};
+  },
+
+  makePositioned: function(element) {
+    element = $(element);
+    var pos = Element.getStyle(element, 'position');
+    if (pos == 'static' || !pos) {
+      element._madePositioned = true;
+      element.style.position = 'relative';
+      // Opera returns the offset relative to the positioning context, when an
+      // element is position relative but top and left have not been defined
+      if (Prototype.Browser.Opera) {
+        element.style.top = 0;
+        element.style.left = 0;
+      }
+    }
+    return element;
+  },
+
+  undoPositioned: function(element) {
+    element = $(element);
+    if (element._madePositioned) {
+      element._madePositioned = undefined;
+      element.style.position =
+        element.style.top =
+        element.style.left =
+        element.style.bottom =
+        element.style.right = '';
+    }
+    return element;
+  },
+
+  makeClipping: function(element) {
+    element = $(element);
+    if (element._overflow) return element;
+    element._overflow = Element.getStyle(element, 'overflow') || 'auto';
+    if (element._overflow !== 'hidden')
+      element.style.overflow = 'hidden';
+    return element;
+  },
+
+  undoClipping: function(element) {
+    element = $(element);
+    if (!element._overflow) return element;
+    element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
+    element._overflow = null;
+    return element;
+  },
+
+  cumulativeOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      element = element.offsetParent;
+    } while (element);
+    return Element._returnOffset(valueL, valueT);
+  },
+
+  positionedOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      element = element.offsetParent;
+      if (element) {
+        if (element.tagName.toUpperCase() == 'BODY') break;
+        var p = Element.getStyle(element, 'position');
+        if (p !== 'static') break;
+      }
+    } while (element);
+    return Element._returnOffset(valueL, valueT);
+  },
+
+  absolutize: function(element) {
+    element = $(element);
+    if (element.getStyle('position') == 'absolute') return element;
+    // Position.prepare(); // To be done manually by Scripty when it needs it.
+
+    var offsets = element.positionedOffset();
+    var top     = offsets[1];
+    var left    = offsets[0];
+    var width   = element.clientWidth;
+    var height  = element.clientHeight;
+
+    element._originalLeft   = left - parseFloat(element.style.left  || 0);
+    element._originalTop    = top  - parseFloat(element.style.top || 0);
+    element._originalWidth  = element.style.width;
+    element._originalHeight = element.style.height;
+
+    element.style.position = 'absolute';
+    element.style.top    = top + 'px';
+    element.style.left   = left + 'px';
+    element.style.width  = width + 'px';
+    element.style.height = height + 'px';
+    return element;
+  },
+
+  relativize: function(element) {
+    element = $(element);
+    if (element.getStyle('position') == 'relative') return element;
+    // Position.prepare(); // To be done manually by Scripty when it needs it.
+
+    element.style.position = 'relative';
+    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
+    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
+
+    element.style.top    = top + 'px';
+    element.style.left   = left + 'px';
+    element.style.height = element._originalHeight;
+    element.style.width  = element._originalWidth;
+    return element;
+  },
+
+  cumulativeScrollOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.scrollTop  || 0;
+      valueL += element.scrollLeft || 0;
+      element = element.parentNode;
+    } while (element);
+    return Element._returnOffset(valueL, valueT);
+  },
+
+  getOffsetParent: function(element) {
+    element = $(element);
+    var op = element.offsetParent, body = document.body, docEl = document.documentElement;
+
+    /* IE with strict doctype may try to return documentElement as offsetParent
+       on relatively positioned elements, we will return body instead */
+    if (op && op !== docEl) return $(op);
+    if (op === docEl || element === docEl || element === body) return $(body);
+
+    while ((element = element.parentNode) && element !== body)
+      if (Element.getStyle(element, 'position') != 'static')
+        return $(element);
+
+    return $(body);
+  },
+
+  viewportOffset: function(forElement) {
+    forElement = $(forElement);
+
+    var element = forElement, valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+    } while ((element = element.getOffsetParent()) != document.body);
+
+    element = forElement;
+    do {
+      if (!Prototype.Browser.Opera || (element.tagName && (element.tagName.toUpperCase() == 'BODY'))) {
+        valueT -= element.scrollTop  || 0;
+        valueL -= element.scrollLeft || 0;
+      }
+    } while (element = element.parentNode);
+
+    return Element._returnOffset(valueL, valueT);
+  },
+
+  clonePosition: function(element, source) {
+    var options = Object.extend({
+      setLeft:    true,
+      setTop:     true,
+      setWidth:   true,
+      setHeight:  true,
+      offsetTop:  0,
+      offsetLeft: 0
+    }, arguments[2] || { });
+
+    // find page position of source
+    source = $(source);
+    var p = source.viewportOffset();
+
+    // find coordinate system to use
+    element = $(element);
+    var delta = [0, 0];
+    var parent = null;
+    // delta [0,0] will do fine with position: fixed elements,
+    // position:absolute needs offsetParent deltas
+    if (Element.getStyle(element, 'position') == 'absolute') {
+      parent = element.getOffsetParent();
+      delta = parent.viewportOffset();
+    }
+
+    // correct by body offsets (fixes Safari)
+    if (parent == document.body) {
+      delta[0] -= document.body.offsetLeft;
+      delta[1] -= document.body.offsetTop;
+    }
+
+    // set position
+    if (options.setLeft)   element.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
+    if (options.setTop)    element.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
+    if (options.setWidth)  element.style.width = source.offsetWidth + 'px';
+    if (options.setHeight) element.style.height = source.offsetHeight + 'px';
+    return element;
+  }
+};
+
+Element.Methods.identify.counter = 1;
+
+Object.extend(Element.Methods, {
+  getElementsBySelector: Element.Methods.select,
+  childElements: Element.Methods.immediateDescendants
+});
+
+Element._attributeTranslations = {
+  write: {
+    names: {
+      className: 'class',
+      htmlFor:   'for'
+    },
+    values: { }
+  }
+};
+
+if (Prototype.Browser.Opera) {
+  Element.Methods.getStyle = Element.Methods.getStyle.wrap(
+    function(proceed, element, style) {
+      switch (style) {
+        case 'left': case 'top': case 'right': case 'bottom':
+          if (proceed(element, 'position') === 'static') return null;
+        case 'height': case 'width':
+          // returns '0px' for hidden elements; we want it to return null
+          if (!Element.visible(element)) return null;
+
+          // returns the border-box dimensions rather than the content-box
+          // dimensions, so we subtract padding and borders from the value
+          var dim = parseInt(proceed(element, style), 10);
+
+          if (dim !== element['offset' + style.capitalize()])
+            return dim + 'px';
+
+          var properties;
+          if (style === 'height') {
+            properties = ['border-top-width', 'padding-top',
+             'padding-bottom', 'border-bottom-width'];
+          }
+          else {
+            properties = ['border-left-width', 'padding-left',
+             'padding-right', 'border-right-width'];
+          }
+          return properties.inject(dim, function(memo, property) {
+            var val = proceed(element, property);
+            return val === null ? memo : memo - parseInt(val, 10);
+          }) + 'px';
+        default: return proceed(element, style);
+      }
+    }
+  );
+
+  Element.Methods.readAttribute = Element.Methods.readAttribute.wrap(
+    function(proceed, element, attribute) {
+      if (attribute === 'title') return element.title;
+      return proceed(element, attribute);
+    }
+  );
+}
+
+else if (Prototype.Browser.IE) {
+  // IE doesn't report offsets correctly for static elements, so we change them
+  // to "relative" to get the values, then change them back.
+  Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap(
+    function(proceed, element) {
+      element = $(element);
+      // IE throws an error if element is not in document
+      try { element.offsetParent }
+      catch(e) { return $(document.body) }
+      var position = element.getStyle('position');
+      if (position !== 'static') return proceed(element);
+      element.setStyle({ position: 'relative' });
+      var value = proceed(element);
+      element.setStyle({ position: position });
+      return value;
+    }
+  );
+
+  $w('positionedOffset viewportOffset').each(function(method) {
+    Element.Methods[method] = Element.Methods[method].wrap(
+      function(proceed, element) {
+        element = $(element);
+        try { element.offsetParent }
+        catch(e) { return Element._returnOffset(0,0) }
+        var position = element.getStyle('position');
+        if (position !== 'static') return proceed(element);
+        // Trigger hasLayout on the offset parent so that IE6 reports
+        // accurate offsetTop and offsetLeft values for position: fixed.
+        var offsetParent = element.getOffsetParent();
+        if (offsetParent && offsetParent.getStyle('position') === 'fixed')
+          offsetParent.setStyle({ zoom: 1 });
+        element.setStyle({ position: 'relative' });
+        var value = proceed(element);
+        element.setStyle({ position: position });
+        return value;
+      }
+    );
+  });
+
+  Element.Methods.cumulativeOffset = Element.Methods.cumulativeOffset.wrap(
+    function(proceed, element) {
+      try { element.offsetParent }
+      catch(e) { return Element._returnOffset(0,0) }
+      return proceed(element);
+    }
+  );
+
+  Element.Methods.getStyle = function(element, style) {
+    element = $(element);
+    style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
+    var value = element.style[style];
+    if (!value && element.currentStyle) value = element.currentStyle[style];
+
+    if (style == 'opacity') {
+      if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
+        if (value[1]) return parseFloat(value[1]) / 100;
+      return 1.0;
+    }
+
+    if (value == 'auto') {
+      if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
+        return element['offset' + style.capitalize()] + 'px';
+      return null;
+    }
+    return value;
+  };
+
+  Element.Methods.setOpacity = function(element, value) {
+    function stripAlpha(filter){
+      return filter.replace(/alpha\([^\)]*\)/gi,'');
+    }
+    element = $(element);
+    var currentStyle = element.currentStyle;
+    if ((currentStyle && !currentStyle.hasLayout) ||
+      (!currentStyle && element.style.zoom == 'normal'))
+        element.style.zoom = 1;
+
+    var filter = element.getStyle('filter'), style = element.style;
+    if (value == 1 || value === '') {
+      (filter = stripAlpha(filter)) ?
+        style.filter = filter : style.removeAttribute('filter');
+      return element;
+    } else if (value < 0.00001) value = 0;
+    style.filter = stripAlpha(filter) +
+      'alpha(opacity=' + (value * 100) + ')';
+    return element;
+  };
+
+  Element._attributeTranslations = {
+    read: {
+      names: {
+        'class': 'className',
+        'for':   'htmlFor'
+      },
+      values: {
+        _getAttr: function(element, attribute) {
+          return element.getAttribute(attribute, 2);
+        },
+        _getAttrNode: function(element, attribute) {
+          var node = element.getAttributeNode(attribute);
+          return node ? node.value : "";
+        },
+        _getEv: function(element, attribute) {
+          attribute = element.getAttribute(attribute);
+          return attribute ? attribute.toString().slice(23, -2) : null;
+        },
+        _flag: function(element, attribute) {
+          return $(element).hasAttribute(attribute) ? attribute : null;
+        },
+        style: function(element) {
+          return element.style.cssText.toLowerCase();
+        },
+        title: function(element) {
+          return element.title;
+        }
+      }
+    }
+  };
+
+  Element._attributeTranslations.write = {
+    names: Object.extend({
+      cellpadding: 'cellPadding',
+      cellspacing: 'cellSpacing'
+    }, Element._attributeTranslations.read.names),
+    values: {
+      checked: function(element, value) {
+        element.checked = !!value;
+      },
+
+      style: function(element, value) {
+        element.style.cssText = value ? value : '';
+      }
+    }
+  };
+
+  Element._attributeTranslations.has = {};
+
+  $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
+      'encType maxLength readOnly longDesc frameBorder').each(function(attr) {
+    Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
+    Element._attributeTranslations.has[attr.toLowerCase()] = attr;
+  });
+
+  (function(v) {
+    Object.extend(v, {
+      href:        v._getAttr,
+      src:         v._getAttr,
+      type:        v._getAttr,
+      action:      v._getAttrNode,
+      disabled:    v._flag,
+      checked:     v._flag,
+      readonly:    v._flag,
+      multiple:    v._flag,
+      onload:      v._getEv,
+      onunload:    v._getEv,
+      onclick:     v._getEv,
+      ondblclick:  v._getEv,
+      onmousedown: v._getEv,
+      onmouseup:   v._getEv,
+      onmouseover: v._getEv,
+      onmousemove: v._getEv,
+      onmouseout:  v._getEv,
+      onfocus:     v._getEv,
+      onblur:      v._getEv,
+      onkeypress:  v._getEv,
+      onkeydown:   v._getEv,
+      onkeyup:     v._getEv,
+      onsubmit:    v._getEv,
+      onreset:     v._getEv,
+      onselect:    v._getEv,
+      onchange:    v._getEv
+    });
+  })(Element._attributeTranslations.read.values);
+}
+
+else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
+  Element.Methods.setOpacity = function(element, value) {
+    element = $(element);
+    element.style.opacity = (value == 1) ? 0.999999 :
+      (value === '') ? '' : (value < 0.00001) ? 0 : value;
+    return element;
+  };
+}
+
+else if (Prototype.Browser.WebKit) {
+  Element.Methods.setOpacity = function(element, value) {
+    element = $(element);
+    element.style.opacity = (value == 1 || value === '') ? '' :
+      (value < 0.00001) ? 0 : value;
+
+    if (value == 1)
+      if(element.tagName.toUpperCase() == 'IMG' && element.width) {
+        element.width++; element.width--;
+      } else try {
+        var n = document.createTextNode(' ');
+        element.appendChild(n);
+        element.removeChild(n);
+      } catch (e) { }
+
+    return element;
+  };
+
+  // Safari returns margins on body which is incorrect if the child is absolutely
+  // positioned.  For performance reasons, redefine Element#cumulativeOffset for
+  // KHTML/WebKit only.
+  Element.Methods.cumulativeOffset = function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      if (element.offsetParent == document.body)
+        if (Element.getStyle(element, 'position') == 'absolute') break;
+
+      element = element.offsetParent;
+    } while (element);
+
+    return Element._returnOffset(valueL, valueT);
+  };
+}
+
+if (Prototype.Browser.IE || Prototype.Browser.Opera) {
+  // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements
+  Element.Methods.update = function(element, content) {
+    element = $(element);
+
+    if (content && content.toElement) content = content.toElement();
+    if (Object.isElement(content)) return element.update().insert(content);
+
+    content = Object.toHTML(content);
+    var tagName = element.tagName.toUpperCase();
+
+    if (tagName in Element._insertionTranslations.tags) {
+      $A(element.childNodes).each(function(node) { element.removeChild(node) });
+      Element._getContentFromAnonymousElement(tagName, content.stripScripts())
+        .each(function(node) { element.appendChild(node) });
+    }
+    else element.innerHTML = content.stripScripts();
+
+    content.evalScripts.bind(content).defer();
+    return element;
+  };
+}
+
+if ('outerHTML' in document.createElement('div')) {
+  Element.Methods.replace = function(element, content) {
+    element = $(element);
+
+    if (content && content.toElement) content = content.toElement();
+    if (Object.isElement(content)) {
+      element.parentNode.replaceChild(content, element);
+      return element;
+    }
+
+    content = Object.toHTML(content);
+    var parent = element.parentNode, tagName = parent.tagName.toUpperCase();
+
+    if (Element._insertionTranslations.tags[tagName]) {
+      var nextSibling = element.next();
+      var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
+      parent.removeChild(element);
+      if (nextSibling)
+        fragments.each(function(node) { parent.insertBefore(node, nextSibling) });
+      else
+        fragments.each(function(node) { parent.appendChild(node) });
+    }
+    else element.outerHTML = content.stripScripts();
+
+    content.evalScripts.bind(content).defer();
+    return element;
+  };
+}
+
+Element._returnOffset = function(l, t) {
+  var result = [l, t];
+  result.left = l;
+  result.top = t;
+  return result;
+};
+
+Element._getContentFromAnonymousElement = function(tagName, html) {
+  var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];
+  if (t) {
+    div.innerHTML = t[0] + html + t[1];
+    t[2].times(function() { div = div.firstChild });
+  } else div.innerHTML = html;
+  return $A(div.childNodes);
+};
+
+Element._insertionTranslations = {
+  before: function(element, node) {
+    element.parentNode.insertBefore(node, element);
+  },
+  top: function(element, node) {
+    element.insertBefore(node, element.firstChild);
+  },
+  bottom: function(element, node) {
+    element.appendChild(node);
+  },
+  after: function(element, node) {
+    element.parentNode.insertBefore(node, element.nextSibling);
+  },
+  tags: {
+    TABLE:  ['<table>',                '</table>',                   1],
+    TBODY:  ['<table><tbody>',         '</tbody></table>',           2],
+    TR:     ['<table><tbody><tr>',     '</tr></tbody></table>',      3],
+    TD:     ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
+    SELECT: ['<select>',               '</select>',                  1]
+  }
+};
+
+(function() {
+  Object.extend(this.tags, {
+    THEAD: this.tags.TBODY,
+    TFOOT: this.tags.TBODY,
+    TH:    this.tags.TD
+  });
+}).call(Element._insertionTranslations);
+
+Element.Methods.Simulated = {
+  hasAttribute: function(element, attribute) {
+    attribute = Element._attributeTranslations.has[attribute] || attribute;
+    var node = $(element).getAttributeNode(attribute);
+    return !!(node && node.specified);
+  }
+};
+
+Element.Methods.ByTag = { };
+
+Object.extend(Element, Element.Methods);
+
+if (!Prototype.BrowserFeatures.ElementExtensions &&
+    document.createElement('div')['__proto__']) {
+  window.HTMLElement = { };
+  window.HTMLElement.prototype = document.createElement('div')['__proto__'];
+  Prototype.BrowserFeatures.ElementExtensions = true;
+}
+
+Element.extend = (function() {
+  if (Prototype.BrowserFeatures.SpecificElementExtensions)
+    return Prototype.K;
+
+  var Methods = { }, ByTag = Element.Methods.ByTag;
+
+  var extend = Object.extend(function(element) {
+    if (!element || element._extendedByPrototype ||
+        element.nodeType != 1 || element == window) return element;
+
+    var methods = Object.clone(Methods),
+      tagName = element.tagName.toUpperCase(), property, value;
+
+    // extend methods for specific tags
+    if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);
+
+    for (property in methods) {
+      value = methods[property];
+      if (Object.isFunction(value) && !(property in element))
+        element[property] = value.methodize();
+    }
+
+    element._extendedByPrototype = Prototype.emptyFunction;
+    return element;
+
+  }, {
+    refresh: function() {
+      // extend methods for all tags (Safari doesn't need this)
+      if (!Prototype.BrowserFeatures.ElementExtensions) {
+        Object.extend(Methods, Element.Methods);
+        Object.extend(Methods, Element.Methods.Simulated);
+      }
+    }
+  });
+
+  extend.refresh();
+  return extend;
+})();
+
+Element.hasAttribute = function(element, attribute) {
+  if (element.hasAttribute) return element.hasAttribute(attribute);
+  return Element.Methods.Simulated.hasAttribute(element, attribute);
+};
+
+Element.addMethods = function(methods) {
+  var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
+
+  if (!methods) {
+    Object.extend(Form, Form.Methods);
+    Object.extend(Form.Element, Form.Element.Methods);
+    Object.extend(Element.Methods.ByTag, {
+      "FORM":     Object.clone(Form.Methods),
+      "INPUT":    Object.clone(Form.Element.Methods),
+      "SELECT":   Object.clone(Form.Element.Methods),
+      "TEXTAREA": Object.clone(Form.Element.Methods)
+    });
+  }
+
+  if (arguments.length == 2) {
+    var tagName = methods;
+    methods = arguments[1];
+  }
+
+  if (!tagName) Object.extend(Element.Methods, methods || { });
+  else {
+    if (Object.isArray(tagName)) tagName.each(extend);
+    else extend(tagName);
+  }
+
+  function extend(tagName) {
+    tagName = tagName.toUpperCase();
+    if (!Element.Methods.ByTag[tagName])
+      Element.Methods.ByTag[tagName] = { };
+    Object.extend(Element.Methods.ByTag[tagName], methods);
+  }
+
+  function copy(methods, destination, onlyIfAbsent) {
+    onlyIfAbsent = onlyIfAbsent || false;
+    for (var property in methods) {
+      var value = methods[property];
+      if (!Object.isFunction(value)) continue;
+      if (!onlyIfAbsent || !(property in destination))
+        destination[property] = value.methodize();
+    }
+  }
+
+  function findDOMClass(tagName) {
+    var klass;
+    var trans = {
+      "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
+      "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
+      "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
+      "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
+      "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
+      "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
+      "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
+      "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
+      "FrameSet", "IFRAME": "IFrame"
+    };
+    if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
+    if (window[klass]) return window[klass];
+    klass = 'HTML' + tagName + 'Element';
+    if (window[klass]) return window[klass];
+    klass = 'HTML' + tagName.capitalize() + 'Element';
+    if (window[klass]) return window[klass];
+
+    window[klass] = { };
+    window[klass].prototype = document.createElement(tagName)['__proto__'];
+    return window[klass];
+  }
+
+  if (F.ElementExtensions) {
+    copy(Element.Methods, HTMLElement.prototype);
+    copy(Element.Methods.Simulated, HTMLElement.prototype, true);
+  }
+
+  if (F.SpecificElementExtensions) {
+    for (var tag in Element.Methods.ByTag) {
+      var klass = findDOMClass(tag);
+      if (Object.isUndefined(klass)) continue;
+      copy(T[tag], klass.prototype);
+    }
+  }
+
+  Object.extend(Element, Element.Methods);
+  delete Element.ByTag;
+
+  if (Element.extend.refresh) Element.extend.refresh();
+  Element.cache = { };
+};
+
+document.viewport = {
+  getDimensions: function() {
+    var dimensions = { }, B = Prototype.Browser;
+    $w('width height').each(function(d) {
+      var D = d.capitalize();
+      if (B.WebKit && !document.evaluate) {
+        // Safari <3.0 needs self.innerWidth/Height
+        dimensions[d] = self['inner' + D];
+      } else if (B.Opera && parseFloat(window.opera.version()) < 9.5) {
+        // Opera <9.5 needs document.body.clientWidth/Height
+        dimensions[d] = document.body['client' + D]
+      } else {
+        dimensions[d] = document.documentElement['client' + D];
+      }
+    });
+    return dimensions;
+  },
+
+  getWidth: function() {
+    return this.getDimensions().width;
+  },
+
+  getHeight: function() {
+    return this.getDimensions().height;
+  },
+
+  getScrollOffsets: function() {
+    return Element._returnOffset(
+      window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
+      window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
+  }
+};
+/* Portions of the Selector class are derived from Jack Slocum's DomQuery,
+ * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
+ * license.  Please see http://www.yui-ext.com/ for more information. */
+
+var Selector = Class.create({
+  initialize: function(expression) {
+    this.expression = expression.strip();
+
+    if (this.shouldUseSelectorsAPI()) {
+      this.mode = 'selectorsAPI';
+    } else if (this.shouldUseXPath()) {
+      this.mode = 'xpath';
+      this.compileXPathMatcher();
+    } else {
+      this.mode = "normal";
+      this.compileMatcher();
+    }
+
+  },
+
+  shouldUseXPath: function() {
+    if (!Prototype.BrowserFeatures.XPath) return false;
+
+    var e = this.expression;
+
+    // Safari 3 chokes on :*-of-type and :empty
+    if (Prototype.Browser.WebKit &&
+     (e.include("-of-type") || e.include(":empty")))
+      return false;
+
+    // XPath can't do namespaced attributes, nor can it read
+    // the "checked" property from DOM nodes
+    if ((/(\[[\w-]*?:|:checked)/).test(e))
+      return false;
+
+    return true;
+  },
+
+  shouldUseSelectorsAPI: function() {
+    if (!Prototype.BrowserFeatures.SelectorsAPI) return false;
+
+    if (!Selector._div) Selector._div = new Element('div');
+
+    // Make sure the browser treats the selector as valid. Test on an
+    // isolated element to minimize cost of this check.
+    try {
+      Selector._div.querySelector(this.expression);
+    } catch(e) {
+      return false;
+    }
+
+    return true;
+  },
+
+  compileMatcher: function() {
+    var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
+        c = Selector.criteria, le, p, m;
+
+    if (Selector._cache[e]) {
+      this.matcher = Selector._cache[e];
+      return;
+    }
+
+    this.matcher = ["this.matcher = function(root) {",
+                    "var r = root, h = Selector.handlers, c = false, n;"];
+
+    while (e && le != e && (/\S/).test(e)) {
+      le = e;
+      for (var i in ps) {
+        p = ps[i];
+        if (m = e.match(p)) {
+          this.matcher.push(Object.isFunction(c[i]) ? c[i](m) :
+            new Template(c[i]).evaluate(m));
+          e = e.replace(m[0], '');
+          break;
+        }
+      }
+    }
+
+    this.matcher.push("return h.unique(n);\n}");
+    eval(this.matcher.join('\n'));
+    Selector._cache[this.expression] = this.matcher;
+  },
+
+  compileXPathMatcher: function() {
+    var e = this.expression, ps = Selector.patterns,
+        x = Selector.xpath, le, m;
+
+    if (Selector._cache[e]) {
+      this.xpath = Selector._cache[e]; return;
+    }
+
+    this.matcher = ['.//*'];
+    while (e && le != e && (/\S/).test(e)) {
+      le = e;
+      for (var i in ps) {
+        if (m = e.match(ps[i])) {
+          this.matcher.push(Object.isFunction(x[i]) ? x[i](m) :
+            new Template(x[i]).evaluate(m));
+          e = e.replace(m[0], '');
+          break;
+        }
+      }
+    }
+
+    this.xpath = this.matcher.join('');
+    Selector._cache[this.expression] = this.xpath;
+  },
+
+  findElements: function(root) {
+    root = root || document;
+    var e = this.expression, results;
+
+    switch (this.mode) {
+      case 'selectorsAPI':
+        // querySelectorAll queries document-wide, then filters to descendants
+        // of the context element. That's not what we want.
+        // Add an explicit context to the selector if necessary.
+        if (root !== document) {
+          var oldId = root.id, id = $(root).identify();
+          e = "#" + id + " " + e;
+        }
+
+        results = $A(root.querySelectorAll(e)).map(Element.extend);
+        root.id = oldId;
+
+        return results;
+      case 'xpath':
+        return document._getElementsByXPath(this.xpath, root);
+      default:
+       return this.matcher(root);
+    }
+  },
+
+  match: function(element) {
+    this.tokens = [];
+
+    var e = this.expression, ps = Selector.patterns, as = Selector.assertions;
+    var le, p, m;
+
+    while (e && le !== e && (/\S/).test(e)) {
+      le = e;
+      for (var i in ps) {
+        p = ps[i];
+        if (m = e.match(p)) {
+          // use the Selector.assertions methods unless the selector
+          // is too complex.
+          if (as[i]) {
+            this.tokens.push([i, Object.clone(m)]);
+            e = e.replace(m[0], '');
+          } else {
+            // reluctantly do a document-wide search
+            // and look for a match in the array
+            return this.findElements(document).include(element);
+          }
+        }
+      }
+    }
+
+    var match = true, name, matches;
+    for (var i = 0, token; token = this.tokens[i]; i++) {
+      name = token[0], matches = token[1];
+      if (!Selector.assertions[name](element, matches)) {
+        match = false; break;
+      }
+    }
+
+    return match;
+  },
+
+  toString: function() {
+    return this.expression;
+  },
+
+  inspect: function() {
+    return "#<Selector:" + this.expression.inspect() + ">";
+  }
+});
+
+Object.extend(Selector, {
+  _cache: { },
+
+  xpath: {
+    descendant:   "//*",
+    child:        "/*",
+    adjacent:     "/following-sibling::*[1]",
+    laterSibling: '/following-sibling::*',
+    tagName:      function(m) {
+      if (m[1] == '*') return '';
+      return "[local-name()='" + m[1].toLowerCase() +
+             "' or local-name()='" + m[1].toUpperCase() + "']";
+    },
+    className:    "[contains(concat(' ', @class, ' '), ' #{1} ')]",
+    id:           "[@id='#{1}']",
+    attrPresence: function(m) {
+      m[1] = m[1].toLowerCase();
+      return new Template("[@#{1}]").evaluate(m);
+    },
+    attr: function(m) {
+      m[1] = m[1].toLowerCase();
+      m[3] = m[5] || m[6];
+      return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
+    },
+    pseudo: function(m) {
+      var h = Selector.xpath.pseudos[m[1]];
+      if (!h) return '';
+      if (Object.isFunction(h)) return h(m);
+      return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
+    },
+    operators: {
+      '=':  "[@#{1}='#{3}']",
+      '!=': "[@#{1}!='#{3}']",
+      '^=': "[starts-with(@#{1}, '#{3}')]",
+      '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
+      '*=': "[contains(@#{1}, '#{3}')]",
+      '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
+      '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
+    },
+    pseudos: {
+      'first-child': '[not(preceding-sibling::*)]',
+      'last-child':  '[not(following-sibling::*)]',
+      'only-child':  '[not(preceding-sibling::* or following-sibling::*)]',
+      'empty':       "[count(*) = 0 and (count(text()) = 0)]",
+      'checked':     "[@checked]",
+      'disabled':    "[(@disabled) and (@type!='hidden')]",
+      'enabled':     "[not(@disabled) and (@type!='hidden')]",
+      'not': function(m) {
+        var e = m[6], p = Selector.patterns,
+            x = Selector.xpath, le, v;
+
+        var exclusion = [];
+        while (e && le != e && (/\S/).test(e)) {
+          le = e;
+          for (var i in p) {
+            if (m = e.match(p[i])) {
+              v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m);
+              exclusion.push("(" + v.substring(1, v.length - 1) + ")");
+              e = e.replace(m[0], '');
+              break;
+            }
+          }
+        }
+        return "[not(" + exclusion.join(" and ") + ")]";
+      },
+      'nth-child':      function(m) {
+        return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
+      },
+      'nth-last-child': function(m) {
+        return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
+      },
+      'nth-of-type':    function(m) {
+        return Selector.xpath.pseudos.nth("position() ", m);
+      },
+      'nth-last-of-type': function(m) {
+        return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
+      },
+      'first-of-type':  function(m) {
+        m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
+      },
+      'last-of-type':   function(m) {
+        m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
+      },
+      'only-of-type':   function(m) {
+        var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
+      },
+      nth: function(fragment, m) {
+        var mm, formula = m[6], predicate;
+        if (formula == 'even') formula = '2n+0';
+        if (formula == 'odd')  formula = '2n+1';
+        if (mm = formula.match(/^(\d+)$/)) // digit only
+          return '[' + fragment + "= " + mm[1] + ']';
+        if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
+          if (mm[1] == "-") mm[1] = -1;
+          var a = mm[1] ? Number(mm[1]) : 1;
+          var b = mm[2] ? Number(mm[2]) : 0;
+          predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
+          "((#{fragment} - #{b}) div #{a} >= 0)]";
+          return new Template(predicate).evaluate({
+            fragment: fragment, a: a, b: b });
+        }
+      }
+    }
+  },
+
+  criteria: {
+    tagName:      'n = h.tagName(n, r, "#{1}", c);      c = false;',
+    className:    'n = h.className(n, r, "#{1}", c);    c = false;',
+    id:           'n = h.id(n, r, "#{1}", c);           c = false;',
+    attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;',
+    attr: function(m) {
+      m[3] = (m[5] || m[6]);
+      return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m);
+    },
+    pseudo: function(m) {
+      if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
+      return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
+    },
+    descendant:   'c = "descendant";',
+    child:        'c = "child";',
+    adjacent:     'c = "adjacent";',
+    laterSibling: 'c = "laterSibling";'
+  },
+
+  patterns: {
+    // combinators must be listed first
+    // (and descendant needs to be last combinator)
+    laterSibling: /^\s*~\s*/,
+    child:        /^\s*>\s*/,
+    adjacent:     /^\s*\+\s*/,
+    descendant:   /^\s/,
+
+    // selectors follow
+    tagName:      /^\s*(\*|[\w\-]+)(\b|$)?/,
+    id:           /^#([\w\-\*]+)(\b|$)/,
+    className:    /^\.([\w\-\*]+)(\b|$)/,
+    pseudo:
+/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/,
+    attrPresence: /^\[((?:[\w]+:)?[\w]+)\]/,
+    attr:         /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
+  },
+
+  // for Selector.match and Element#match
+  assertions: {
+    tagName: function(element, matches) {
+      return matches[1].toUpperCase() == element.tagName.toUpperCase();
+    },
+
+    className: function(element, matches) {
+      return Element.hasClassName(element, matches[1]);
+    },
+
+    id: function(element, matches) {
+      return element.id === matches[1];
+    },
+
+    attrPresence: function(element, matches) {
+      return Element.hasAttribute(element, matches[1]);
+    },
+
+    attr: function(element, matches) {
+      var nodeValue = Element.readAttribute(element, matches[1]);
+      return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]);
+    }
+  },
+
+  handlers: {
+    // UTILITY FUNCTIONS
+    // joins two collections
+    concat: function(a, b) {
+      for (var i = 0, node; node = b[i]; i++)
+        a.push(node);
+      return a;
+    },
+
+    // marks an array of nodes for counting
+    mark: function(nodes) {
+      var _true = Prototype.emptyFunction;
+      for (var i = 0, node; node = nodes[i]; i++)
+        node._countedByPrototype = _true;
+      return nodes;
+    },
+
+    unmark: function(nodes) {
+      for (var i = 0, node; node = nodes[i]; i++)
+        node._countedByPrototype = undefined;
+      return nodes;
+    },
+
+    // mark each child node with its position (for nth calls)
+    // "ofType" flag indicates whether we're indexing for nth-of-type
+    // rather than nth-child
+    index: function(parentNode, reverse, ofType) {
+      parentNode._countedByPrototype = Prototype.emptyFunction;
+      if (reverse) {
+        for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
+          var node = nodes[i];
+          if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
+        }
+      } else {
+        for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
+          if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
+      }
+    },
+
+    // filters out duplicates and extends all nodes
+    unique: function(nodes) {
+      if (nodes.length == 0) return nodes;
+      var results = [], n;
+      for (var i = 0, l = nodes.length; i < l; i++)
+        if (!(n = nodes[i])._countedByPrototype) {
+          n._countedByPrototype = Prototype.emptyFunction;
+          results.push(Element.extend(n));
+        }
+      return Selector.handlers.unmark(results);
+    },
+
+    // COMBINATOR FUNCTIONS
+    descendant: function(nodes) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        h.concat(results, node.getElementsByTagName('*'));
+      return results;
+    },
+
+    child: function(nodes) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        for (var j = 0, child; child = node.childNodes[j]; j++)
+          if (child.nodeType == 1 && child.tagName != '!') results.push(child);
+      }
+      return results;
+    },
+
+    adjacent: function(nodes) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        var next = this.nextElementSibling(node);
+        if (next) results.push(next);
+      }
+      return results;
+    },
+
+    laterSibling: function(nodes) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        h.concat(results, Element.nextSiblings(node));
+      return results;
+    },
+
+    nextElementSibling: function(node) {
+      while (node = node.nextSibling)
+        if (node.nodeType == 1) return node;
+      return null;
+    },
+
+    previousElementSibling: function(node) {
+      while (node = node.previousSibling)
+        if (node.nodeType == 1) return node;
+      return null;
+    },
+
+    // TOKEN FUNCTIONS
+    tagName: function(nodes, root, tagName, combinator) {
+      var uTagName = tagName.toUpperCase();
+      var results = [], h = Selector.handlers;
+      if (nodes) {
+        if (combinator) {
+          // fastlane for ordinary descendant combinators
+          if (combinator == "descendant") {
+            for (var i = 0, node; node = nodes[i]; i++)
+              h.concat(results, node.getElementsByTagName(tagName));
+            return results;
+          } else nodes = this[combinator](nodes);
+          if (tagName == "*") return nodes;
+        }
+        for (var i = 0, node; node = nodes[i]; i++)
+          if (node.tagName.toUpperCase() === uTagName) results.push(node);
+        return results;
+      } else return root.getElementsByTagName(tagName);
+    },
+
+    id: function(nodes, root, id, combinator) {
+      var targetNode = $(id), h = Selector.handlers;
+      if (!targetNode) return [];
+      if (!nodes && root == document) return [targetNode];
+      if (nodes) {
+        if (combinator) {
+          if (combinator == 'child') {
+            for (var i = 0, node; node = nodes[i]; i++)
+              if (targetNode.parentNode == node) return [targetNode];
+          } else if (combinator == 'descendant') {
+            for (var i = 0, node; node = nodes[i]; i++)
+              if (Element.descendantOf(targetNode, node)) return [targetNode];
+          } else if (combinator == 'adjacent') {
+            for (var i = 0, node; node = nodes[i]; i++)
+              if (Selector.handlers.previousElementSibling(targetNode) == node)
+                return [targetNode];
+          } else nodes = h[combinator](nodes);
+        }
+        for (var i = 0, node; node = nodes[i]; i++)
+          if (node == targetNode) return [targetNode];
+        return [];
+      }
+      return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
+    },
+
+    className: function(nodes, root, className, combinator) {
+      if (nodes && combinator) nodes = this[combinator](nodes);
+      return Selector.handlers.byClassName(nodes, root, className);
+    },
+
+    byClassName: function(nodes, root, className) {
+      if (!nodes) nodes = Selector.handlers.descendant([root]);
+      var needle = ' ' + className + ' ';
+      for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
+        nodeClassName = node.className;
+        if (nodeClassName.length == 0) continue;
+        if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
+          results.push(node);
+      }
+      return results;
+    },
+
+    attrPresence: function(nodes, root, attr, combinator) {
+      if (!nodes) nodes = root.getElementsByTagName("*");
+      if (nodes && combinator) nodes = this[combinator](nodes);
+      var results = [];
+      for (var i = 0, node; node = nodes[i]; i++)
+        if (Element.hasAttribute(node, attr)) results.push(node);
+      return results;
+    },
+
+    attr: function(nodes, root, attr, value, operator, combinator) {
+      if (!nodes) nodes = root.getElementsByTagName("*");
+      if (nodes && combinator) nodes = this[combinator](nodes);
+      var handler = Selector.operators[operator], results = [];
+      for (var i = 0, node; node = nodes[i]; i++) {
+        var nodeValue = Element.readAttribute(node, attr);
+        if (nodeValue === null) continue;
+        if (handler(nodeValue, value)) results.push(node);
+      }
+      return results;
+    },
+
+    pseudo: function(nodes, name, value, root, combinator) {
+      if (nodes && combinator) nodes = this[combinator](nodes);
+      if (!nodes) nodes = root.getElementsByTagName("*");
+      return Selector.pseudos[name](nodes, value, root);
+    }
+  },
+
+  pseudos: {
+    'first-child': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        if (Selector.handlers.previousElementSibling(node)) continue;
+          results.push(node);
+      }
+      return results;
+    },
+    'last-child': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        if (Selector.handlers.nextElementSibling(node)) continue;
+          results.push(node);
+      }
+      return results;
+    },
+    'only-child': function(nodes, value, root) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
+          results.push(node);
+      return results;
+    },
+    'nth-child':        function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root);
+    },
+    'nth-last-child':   function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root, true);
+    },
+    'nth-of-type':      function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root, false, true);
+    },
+    'nth-last-of-type': function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root, true, true);
+    },
+    'first-of-type':    function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, "1", root, false, true);
+    },
+    'last-of-type':     function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, "1", root, true, true);
+    },
+    'only-of-type':     function(nodes, formula, root) {
+      var p = Selector.pseudos;
+      return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
+    },
+
+    // handles the an+b logic
+    getIndices: function(a, b, total) {
+      if (a == 0) return b > 0 ? [b] : [];
+      return $R(1, total).inject([], function(memo, i) {
+        if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
+        return memo;
+      });
+    },
+
+    // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
+    nth: function(nodes, formula, root, reverse, ofType) {
+      if (nodes.length == 0) return [];
+      if (formula == 'even') formula = '2n+0';
+      if (formula == 'odd')  formula = '2n+1';
+      var h = Selector.handlers, results = [], indexed = [], m;
+      h.mark(nodes);
+      for (var i = 0, node; node = nodes[i]; i++) {
+        if (!node.parentNode._countedByPrototype) {
+          h.index(node.parentNode, reverse, ofType);
+          indexed.push(node.parentNode);
+        }
+      }
+      if (formula.match(/^\d+$/)) { // just a number
+        formula = Number(formula);
+        for (var i = 0, node; node = nodes[i]; i++)
+          if (node.nodeIndex == formula) results.push(node);
+      } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
+        if (m[1] == "-") m[1] = -1;
+        var a = m[1] ? Number(m[1]) : 1;
+        var b = m[2] ? Number(m[2]) : 0;
+        var indices = Selector.pseudos.getIndices(a, b, nodes.length);
+        for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
+          for (var j = 0; j < l; j++)
+            if (node.nodeIndex == indices[j]) results.push(node);
+        }
+      }
+      h.unmark(nodes);
+      h.unmark(indexed);
+      return results;
+    },
+
+    'empty': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        // IE treats comments as element nodes
+        if (node.tagName == '!' || node.firstChild) continue;
+        results.push(node);
+      }
+      return results;
+    },
+
+    'not': function(nodes, selector, root) {
+      var h = Selector.handlers, selectorType, m;
+      var exclusions = new Selector(selector).findElements(root);
+      h.mark(exclusions);
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (!node._countedByPrototype) results.push(node);
+      h.unmark(exclusions);
+      return results;
+    },
+
+    'enabled': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (!node.disabled && (!node.type || node.type !== 'hidden'))
+          results.push(node);
+      return results;
+    },
+
+    'disabled': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (node.disabled) results.push(node);
+      return results;
+    },
+
+    'checked': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (node.checked) results.push(node);
+      return results;
+    }
+  },
+
+  operators: {
+    '=':  function(nv, v) { return nv == v; },
+    '!=': function(nv, v) { return nv != v; },
+    '^=': function(nv, v) { return nv == v || nv && nv.startsWith(v); },
+    '$=': function(nv, v) { return nv == v || nv && nv.endsWith(v); },
+    '*=': function(nv, v) { return nv == v || nv && nv.include(v); },
+    '$=': function(nv, v) { return nv.endsWith(v); },
+    '*=': function(nv, v) { return nv.include(v); },
+    '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
+    '|=': function(nv, v) { return ('-' + (nv || "").toUpperCase() +
+     '-').include('-' + (v || "").toUpperCase() + '-'); }
+  },
+
+  split: function(expression) {
+    var expressions = [];
+    expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
+      expressions.push(m[1].strip());
+    });
+    return expressions;
+  },
+
+  matchElements: function(elements, expression) {
+    var matches = $$(expression), h = Selector.handlers;
+    h.mark(matches);
+    for (var i = 0, results = [], element; element = elements[i]; i++)
+      if (element._countedByPrototype) results.push(element);
+    h.unmark(matches);
+    return results;
+  },
+
+  findElement: function(elements, expression, index) {
+    if (Object.isNumber(expression)) {
+      index = expression; expression = false;
+    }
+    return Selector.matchElements(elements, expression || '*')[index || 0];
+  },
+
+  findChildElements: function(element, expressions) {
+    expressions = Selector.split(expressions.join(','));
+    var results = [], h = Selector.handlers;
+    for (var i = 0, l = expressions.length, selector; i < l; i++) {
+      selector = new Selector(expressions[i].strip());
+      h.concat(results, selector.findElements(element));
+    }
+    return (l > 1) ? h.unique(results) : results;
+  }
+});
+
+if (Prototype.Browser.IE) {
+  Object.extend(Selector.handlers, {
+    // IE returns comment nodes on getElementsByTagName("*").
+    // Filter them out.
+    concat: function(a, b) {
+      for (var i = 0, node; node = b[i]; i++)
+        if (node.tagName !== "!") a.push(node);
+      return a;
+    },
+
+    // IE improperly serializes _countedByPrototype in (inner|outer)HTML.
+    unmark: function(nodes) {
+      for (var i = 0, node; node = nodes[i]; i++)
+        node.removeAttribute('_countedByPrototype');
+      return nodes;
+    }
+  });
+}
+
+function $$() {
+  return Selector.findChildElements(document, $A(arguments));
+}
+var Form = {
+  reset: function(form) {
+    $(form).reset();
+    return form;
+  },
+
+  serializeElements: function(elements, options) {
+    if (typeof options != 'object') options = { hash: !!options };
+    else if (Object.isUndefined(options.hash)) options.hash = true;
+    var key, value, submitted = false, submit = options.submit;
+
+    var data = elements.inject({ }, function(result, element) {
+      if (!element.disabled && element.name) {
+        key = element.name; value = $(element).getValue();
+        if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted &&
+            submit !== false && (!submit || key == submit) && (submitted = true)))) {
+          if (key in result) {
+            // a key is already present; construct an array of values
+            if (!Object.isArray(result[key])) result[key] = [result[key]];
+            result[key].push(value);
+          }
+          else result[key] = value;
+        }
+      }
+      return result;
+    });
+
+    return options.hash ? data : Object.toQueryString(data);
+  }
+};
+
+Form.Methods = {
+  serialize: function(form, options) {
+    return Form.serializeElements(Form.getElements(form), options);
+  },
+
+  getElements: function(form) {
+    return $A($(form).getElementsByTagName('*')).inject([],
+      function(elements, child) {
+        if (Form.Element.Serializers[child.tagName.toLowerCase()])
+          elements.push(Element.extend(child));
+        return elements;
+      }
+    );
+  },
+
+  getInputs: function(form, typeName, name) {
+    form = $(form);
+    var inputs = form.getElementsByTagName('input');
+
+    if (!typeName && !name) return $A(inputs).map(Element.extend);
+
+    for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
+      var input = inputs[i];
+      if ((typeName && input.type != typeName) || (name && input.name != name))
+        continue;
+      matchingInputs.push(Element.extend(input));
+    }
+
+    return matchingInputs;
+  },
+
+  disable: function(form) {
+    form = $(form);
+    Form.getElements(form).invoke('disable');
+    return form;
+  },
+
+  enable: function(form) {
+    form = $(form);
+    Form.getElements(form).invoke('enable');
+    return form;
+  },
+
+  findFirstElement: function(form) {
+    var elements = $(form).getElements().findAll(function(element) {
+      return 'hidden' != element.type && !element.disabled;
+    });
+    var firstByIndex = elements.findAll(function(element) {
+      return element.hasAttribute('tabIndex') && element.tabIndex >= 0;
+    }).sortBy(function(element) { return element.tabIndex }).first();
+
+    return firstByIndex ? firstByIndex : elements.find(function(element) {
+      return ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
+    });
+  },
+
+  focusFirstElement: function(form) {
+    form = $(form);
+    form.findFirstElement().activate();
+    return form;
+  },
+
+  request: function(form, options) {
+    form = $(form), options = Object.clone(options || { });
+
+    var params = options.parameters, action = form.readAttribute('action') || '';
+    if (action.blank()) action = window.location.href;
+    options.parameters = form.serialize(true);
+
+    if (params) {
+      if (Object.isString(params)) params = params.toQueryParams();
+      Object.extend(options.parameters, params);
+    }
+
+    if (form.hasAttribute('method') && !options.method)
+      options.method = form.method;
+
+    return new Ajax.Request(action, options);
+  }
+};
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element = {
+  focus: function(element) {
+    $(element).focus();
+    return element;
+  },
+
+  select: function(element) {
+    $(element).select();
+    return element;
+  }
+};
+
+Form.Element.Methods = {
+  serialize: function(element) {
+    element = $(element);
+    if (!element.disabled && element.name) {
+      var value = element.getValue();
+      if (value != undefined) {
+        var pair = { };
+        pair[element.name] = value;
+        return Object.toQueryString(pair);
+      }
+    }
+    return '';
+  },
+
+  getValue: function(element) {
+    element = $(element);
+    var method = element.tagName.toLowerCase();
+    return Form.Element.Serializers[method](element);
+  },
+
+  setValue: function(element, value) {
+    element = $(element);
+    var method = element.tagName.toLowerCase();
+    Form.Element.Serializers[method](element, value);
+    return element;
+  },
+
+  clear: function(element) {
+    $(element).value = '';
+    return element;
+  },
+
+  present: function(element) {
+    return $(element).value != '';
+  },
+
+  activate: function(element) {
+    element = $(element);
+    try {
+      element.focus();
+      if (element.select && (element.tagName.toLowerCase() != 'input' ||
+          !['button', 'reset', 'submit'].include(element.type)))
+        element.select();
+    } catch (e) { }
+    return element;
+  },
+
+  disable: function(element) {
+    element = $(element);
+    element.disabled = true;
+    return element;
+  },
+
+  enable: function(element) {
+    element = $(element);
+    element.disabled = false;
+    return element;
+  }
+};
+
+/*--------------------------------------------------------------------------*/
+
+var Field = Form.Element;
+var $F = Form.Element.Methods.getValue;
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element.Serializers = {
+  input: function(element, value) {
+    switch (element.type.toLowerCase()) {
+      case 'checkbox':
+      case 'radio':
+        return Form.Element.Serializers.inputSelector(element, value);
+      default:
+        return Form.Element.Serializers.textarea(element, value);
+    }
+  },
+
+  inputSelector: function(element, value) {
+    if (Object.isUndefined(value)) return element.checked ? element.value : null;
+    else element.checked = !!value;
+  },
+
+  textarea: function(element, value) {
+    if (Object.isUndefined(value)) return element.value;
+    else element.value = value;
+  },
+
+  select: function(element, value) {
+    if (Object.isUndefined(value))
+      return this[element.type == 'select-one' ?
+        'selectOne' : 'selectMany'](element);
+    else {
+      var opt, currentValue, single = !Object.isArray(value);
+      for (var i = 0, length = element.length; i < length; i++) {
+        opt = element.options[i];
+        currentValue = this.optionValue(opt);
+        if (single) {
+          if (currentValue == value) {
+            opt.selected = true;
+            return;
+          }
+        }
+        else opt.selected = value.include(currentValue);
+      }
+    }
+  },
+
+  selectOne: function(element) {
+    var index = element.selectedIndex;
+    return index >= 0 ? this.optionValue(element.options[index]) : null;
+  },
+
+  selectMany: function(element) {
+    var values, length = element.length;
+    if (!length) return null;
+
+    for (var i = 0, values = []; i < length; i++) {
+      var opt = element.options[i];
+      if (opt.selected) values.push(this.optionValue(opt));
+    }
+    return values;
+  },
+
+  optionValue: function(opt) {
+    // extend element because hasAttribute may not be native
+    return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
+  }
+};
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
+  initialize: function($super, element, frequency, callback) {
+    $super(callback, frequency);
+    this.element   = $(element);
+    this.lastValue = this.getValue();
+  },
+
+  execute: function() {
+    var value = this.getValue();
+    if (Object.isString(this.lastValue) && Object.isString(value) ?
+        this.lastValue != value : String(this.lastValue) != String(value)) {
+      this.callback(this.element, value);
+      this.lastValue = value;
+    }
+  }
+});
+
+Form.Element.Observer = Class.create(Abstract.TimedObserver, {
+  getValue: function() {
+    return Form.Element.getValue(this.element);
+  }
+});
+
+Form.Observer = Class.create(Abstract.TimedObserver, {
+  getValue: function() {
+    return Form.serialize(this.element);
+  }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.EventObserver = Class.create({
+  initialize: function(element, callback) {
+    this.element  = $(element);
+    this.callback = callback;
+
+    this.lastValue = this.getValue();
+    if (this.element.tagName.toLowerCase() == 'form')
+      this.registerFormCallbacks();
+    else
+      this.registerCallback(this.element);
+  },
+
+  onElementEvent: function() {
+    var value = this.getValue();
+    if (this.lastValue != value) {
+      this.callback(this.element, value);
+      this.lastValue = value;
+    }
+  },
+
+  registerFormCallbacks: function() {
+    Form.getElements(this.element).each(this.registerCallback, this);
+  },
+
+  registerCallback: function(element) {
+    if (element.type) {
+      switch (element.type.toLowerCase()) {
+        case 'checkbox':
+        case 'radio':
+          Event.observe(element, 'click', this.onElementEvent.bind(this));
+          break;
+        default:
+          Event.observe(element, 'change', this.onElementEvent.bind(this));
+          break;
+      }
+    }
+  }
+});
+
+Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
+  getValue: function() {
+    return Form.Element.getValue(this.element);
+  }
+});
+
+Form.EventObserver = Class.create(Abstract.EventObserver, {
+  getValue: function() {
+    return Form.serialize(this.element);
+  }
+});
+if (!window.Event) var Event = { };
+
+Object.extend(Event, {
+  KEY_BACKSPACE: 8,
+  KEY_TAB:       9,
+  KEY_RETURN:   13,
+  KEY_ESC:      27,
+  KEY_LEFT:     37,
+  KEY_UP:       38,
+  KEY_RIGHT:    39,
+  KEY_DOWN:     40,
+  KEY_DELETE:   46,
+  KEY_HOME:     36,
+  KEY_END:      35,
+  KEY_PAGEUP:   33,
+  KEY_PAGEDOWN: 34,
+  KEY_INSERT:   45,
+
+  cache: { },
+
+  relatedTarget: function(event) {
+    var element;
+    switch(event.type) {
+      case 'mouseover': element = event.fromElement; break;
+      case 'mouseout':  element = event.toElement;   break;
+      default: return null;
+    }
+    return Element.extend(element);
+  }
+});
+
+Event.Methods = (function() {
+  var isButton;
+
+  if (Prototype.Browser.IE) {
+    var buttonMap = { 0: 1, 1: 4, 2: 2 };
+    isButton = function(event, code) {
+      return event.button == buttonMap[code];
+    };
+
+  } else if (Prototype.Browser.WebKit) {
+    isButton = function(event, code) {
+      switch (code) {
+        case 0: return event.which == 1 && !event.metaKey;
+        case 1: return event.which == 1 && event.metaKey;
+        default: return false;
+      }
+    };
+
+  } else {
+    isButton = function(event, code) {
+      return event.which ? (event.which === code + 1) : (event.button === code);
+    };
+  }
+
+  return {
+    isLeftClick:   function(event) { return isButton(event, 0) },
+    isMiddleClick: function(event) { return isButton(event, 1) },
+    isRightClick:  function(event) { return isButton(event, 2) },
+
+    element: function(event) {
+      event = Event.extend(event);
+
+      var node          = event.target,
+          type          = event.type,
+          currentTarget = event.currentTarget;
+
+      if (currentTarget && currentTarget.tagName) {
+        // Firefox screws up the "click" event when moving between radio buttons
+        // via arrow keys. It also screws up the "load" and "error" events on images,
+        // reporting the document as the target instead of the original image.
+        if (type === 'load' || type === 'error' ||
+          (type === 'click' && currentTarget.tagName.toLowerCase() === 'input'
+            && currentTarget.type === 'radio'))
+              node = currentTarget;
+      }
+      if (node.nodeType == Node.TEXT_NODE) node = node.parentNode;
+      return Element.extend(node);
+    },
+
+    findElement: function(event, expression) {
+      var element = Event.element(event);
+      if (!expression) return element;
+      var elements = [element].concat(element.ancestors());
+      return Selector.findElement(elements, expression, 0);
+    },
+
+    pointer: function(event) {
+      var docElement = document.documentElement,
+      body = document.body || { scrollLeft: 0, scrollTop: 0 };
+      return {
+        x: event.pageX || (event.clientX +
+          (docElement.scrollLeft || body.scrollLeft) -
+          (docElement.clientLeft || 0)),
+        y: event.pageY || (event.clientY +
+          (docElement.scrollTop || body.scrollTop) -
+          (docElement.clientTop || 0))
+      };
+    },
+
+    pointerX: function(event) { return Event.pointer(event).x },
+    pointerY: function(event) { return Event.pointer(event).y },
+
+    stop: function(event) {
+      Event.extend(event);
+      event.preventDefault();
+      event.stopPropagation();
+      event.stopped = true;
+    }
+  };
+})();
+
+Event.extend = (function() {
+  var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
+    m[name] = Event.Methods[name].methodize();
+    return m;
+  });
+
+  if (Prototype.Browser.IE) {
+    Object.extend(methods, {
+      stopPropagation: function() { this.cancelBubble = true },
+      preventDefault:  function() { this.returnValue = false },
+      inspect: function() { return "[object Event]" }
+    });
+
+    return function(event) {
+      if (!event) return false;
+      if (event._extendedByPrototype) return event;
+
+      event._extendedByPrototype = Prototype.emptyFunction;
+      var pointer = Event.pointer(event);
+      Object.extend(event, {
+        target: event.srcElement,
+        relatedTarget: Event.relatedTarget(event),
+        pageX:  pointer.x,
+        pageY:  pointer.y
+      });
+      return Object.extend(event, methods);
+    };
+
+  } else {
+    Event.prototype = Event.prototype || document.createEvent("HTMLEvents")['__proto__'];
+    Object.extend(Event.prototype, methods);
+    return Prototype.K;
+  }
+})();
+
+Object.extend(Event, (function() {
+  var cache = Event.cache;
+
+  function getEventID(element) {
+    if (element._prototypeEventID) return element._prototypeEventID[0];
+    arguments.callee.id = arguments.callee.id || 1;
+    return element._prototypeEventID = [++arguments.callee.id];
+  }
+
+  function getDOMEventName(eventName) {
+    if (eventName && eventName.include(':')) return "dataavailable";
+    return eventName;
+  }
+
+  function getCacheForID(id) {
+    return cache[id] = cache[id] || { };
+  }
+
+  function getWrappersForEventName(id, eventName) {
+    var c = getCacheForID(id);
+    return c[eventName] = c[eventName] || [];
+  }
+
+  function createWrapper(element, eventName, handler) {
+    var id = getEventID(element);
+    var c = getWrappersForEventName(id, eventName);
+    if (c.pluck("handler").include(handler)) return false;
+
+    var wrapper = function(event) {
+      if (!Event || !Event.extend ||
+        (event.eventName && event.eventName != eventName))
+          return false;
+
+      Event.extend(event);
+      handler.call(element, event);
+    };
+
+    wrapper.handler = handler;
+    c.push(wrapper);
+    return wrapper;
+  }
+
+  function findWrapper(id, eventName, handler) {
+    var c = getWrappersForEventName(id, eventName);
+    return c.find(function(wrapper) { return wrapper.handler == handler });
+  }
+
+  function destroyWrapper(id, eventName, handler) {
+    var c = getCacheForID(id);
+    if (!c[eventName]) return false;
+    c[eventName] = c[eventName].without(findWrapper(id, eventName, handler));
+  }
+
+  function destroyCache() {
+    for (var id in cache)
+      for (var eventName in cache[id])
+        cache[id][eventName] = null;
+  }
+
+
+  // Internet Explorer needs to remove event handlers on page unload
+  // in order to avoid memory leaks.
+  if (window.attachEvent) {
+    window.attachEvent("onunload", destroyCache);
+  }
+
+  // Safari has a dummy event handler on page unload so that it won't
+  // use its bfcache. Safari <= 3.1 has an issue with restoring the "document"
+  // object when page is returned to via the back button using its bfcache.
+  if (Prototype.Browser.WebKit) {
+    window.addEventListener('unload', Prototype.emptyFunction, false);
+  }
+
+  return {
+    observe: function(element, eventName, handler) {
+      element = $(element);
+      var name = getDOMEventName(eventName);
+
+      var wrapper = createWrapper(element, eventName, handler);
+      if (!wrapper) return element;
+
+      if (element.addEventListener) {
+        element.addEventListener(name, wrapper, false);
+      } else {
+        element.attachEvent("on" + name, wrapper);
+      }
+
+      return element;
+    },
+
+    stopObserving: function(element, eventName, handler) {
+      element = $(element);
+      var id = getEventID(element), name = getDOMEventName(eventName);
+
+      if (!handler && eventName) {
+        getWrappersForEventName(id, eventName).each(function(wrapper) {
+          element.stopObserving(eventName, wrapper.handler);
+        });
+        return element;
+
+      } else if (!eventName) {
+        Object.keys(getCacheForID(id)).each(function(eventName) {
+          element.stopObserving(eventName);
+        });
+        return element;
+      }
+
+      var wrapper = findWrapper(id, eventName, handler);
+      if (!wrapper) return element;
+
+      if (element.removeEventListener) {
+        element.removeEventListener(name, wrapper, false);
+      } else {
+        element.detachEvent("on" + name, wrapper);
+      }
+
+      destroyWrapper(id, eventName, handler);
+
+      return element;
+    },
+
+    fire: function(element, eventName, memo) {
+      element = $(element);
+      if (element == document && document.createEvent && !element.dispatchEvent)
+        element = document.documentElement;
+
+      var event;
+      if (document.createEvent) {
+        event = document.createEvent("HTMLEvents");
+        event.initEvent("dataavailable", true, true);
+      } else {
+        event = document.createEventObject();
+        event.eventType = "ondataavailable";
+      }
+
+      event.eventName = eventName;
+      event.memo = memo || { };
+
+      if (document.createEvent) {
+        element.dispatchEvent(event);
+      } else {
+        element.fireEvent(event.eventType, event);
+      }
+
+      return Event.extend(event);
+    }
+  };
+})());
+
+Object.extend(Event, Event.Methods);
+
+Element.addMethods({
+  fire:          Event.fire,
+  observe:       Event.observe,
+  stopObserving: Event.stopObserving
+});
+
+Object.extend(document, {
+  fire:          Element.Methods.fire.methodize(),
+  observe:       Element.Methods.observe.methodize(),
+  stopObserving: Element.Methods.stopObserving.methodize(),
+  loaded:        false
+});
+
+(function() {
+  /* Support for the DOMContentLoaded event is based on work by Dan Webb,
+     Matthias Miller, Dean Edwards and John Resig. */
+
+  var timer;
+
+  function fireContentLoadedEvent() {
+    if (document.loaded) return;
+    if (timer) window.clearInterval(timer);
+    document.fire("dom:loaded");
+    document.loaded = true;
+  }
+
+  if (document.addEventListener) {
+    if (Prototype.Browser.WebKit) {
+      timer = window.setInterval(function() {
+        if (/loaded|complete/.test(document.readyState))
+          fireContentLoadedEvent();
+      }, 0);
+
+      Event.observe(window, "load", fireContentLoadedEvent);
+
+    } else {
+      document.addEventListener("DOMContentLoaded",
+        fireContentLoadedEvent, false);
+    }
+
+  } else {
+    document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>");
+    $("__onDOMContentLoaded").onreadystatechange = function() {
+      if (this.readyState == "complete") {
+        this.onreadystatechange = null;
+        fireContentLoadedEvent();
+      }
+    };
+  }
+})();
+/*------------------------------- DEPRECATED -------------------------------*/
+
+Hash.toQueryString = Object.toQueryString;
+
+var Toggle = { display: Element.toggle };
+
+Element.Methods.childOf = Element.Methods.descendantOf;
+
+var Insertion = {
+  Before: function(element, content) {
+    return Element.insert(element, {before:content});
+  },
+
+  Top: function(element, content) {
+    return Element.insert(element, {top:content});
+  },
+
+  Bottom: function(element, content) {
+    return Element.insert(element, {bottom:content});
+  },
+
+  After: function(element, content) {
+    return Element.insert(element, {after:content});
+  }
+};
+
+var $continue = new Error('"throw $continue" is deprecated, use "return" instead');
+
+// This should be moved to script.aculo.us; notice the deprecated methods
+// further below, that map to the newer Element methods.
+var Position = {
+  // set to true if needed, warning: firefox performance problems
+  // NOT neeeded for page scrolling, only if draggable contained in
+  // scrollable elements
+  includeScrollOffsets: false,
+
+  // must be called before calling withinIncludingScrolloffset, every time the
+  // page is scrolled
+  prepare: function() {
+    this.deltaX =  window.pageXOffset
+                || document.documentElement.scrollLeft
+                || document.body.scrollLeft
+                || 0;
+    this.deltaY =  window.pageYOffset
+                || document.documentElement.scrollTop
+                || document.body.scrollTop
+                || 0;
+  },
+
+  // caches x/y coordinate pair to use with overlap
+  within: function(element, x, y) {
+    if (this.includeScrollOffsets)
+      return this.withinIncludingScrolloffsets(element, x, y);
+    this.xcomp = x;
+    this.ycomp = y;
+    this.offset = Element.cumulativeOffset(element);
+
+    return (y >= this.offset[1] &&
+            y <  this.offset[1] + element.offsetHeight &&
+            x >= this.offset[0] &&
+            x <  this.offset[0] + element.offsetWidth);
+  },
+
+  withinIncludingScrolloffsets: function(element, x, y) {
+    var offsetcache = Element.cumulativeScrollOffset(element);
+
+    this.xcomp = x + offsetcache[0] - this.deltaX;
+    this.ycomp = y + offsetcache[1] - this.deltaY;
+    this.offset = Element.cumulativeOffset(element);
+
+    return (this.ycomp >= this.offset[1] &&
+            this.ycomp <  this.offset[1] + element.offsetHeight &&
+            this.xcomp >= this.offset[0] &&
+            this.xcomp <  this.offset[0] + element.offsetWidth);
+  },
+
+  // within must be called directly before
+  overlap: function(mode, element) {
+    if (!mode) return 0;
+    if (mode == 'vertical')
+      return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
+        element.offsetHeight;
+    if (mode == 'horizontal')
+      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
+        element.offsetWidth;
+  },
+
+  // Deprecation layer -- use newer Element methods now (1.5.2).
+
+  cumulativeOffset: Element.Methods.cumulativeOffset,
+
+  positionedOffset: Element.Methods.positionedOffset,
+
+  absolutize: function(element) {
+    Position.prepare();
+    return Element.absolutize(element);
+  },
+
+  relativize: function(element) {
+    Position.prepare();
+    return Element.relativize(element);
+  },
+
+  realOffset: Element.Methods.cumulativeScrollOffset,
+
+  offsetParent: Element.Methods.getOffsetParent,
+
+  page: Element.Methods.viewportOffset,
+
+  clone: function(source, target, options) {
+    options = options || { };
+    return Element.clonePosition(target, source, options);
+  }
+};
+
+/*--------------------------------------------------------------------------*/
+
+if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
+  function iter(name) {
+    return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
+  }
+
+  instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
+  function(element, className) {
+    className = className.toString().strip();
+    var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className);
+    return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
+  } : function(element, className) {
+    className = className.toString().strip();
+    var elements = [], classNames = (/\s/.test(className) ? $w(className) : null);
+    if (!classNames && !className) return elements;
+
+    var nodes = $(element).getElementsByTagName('*');
+    className = ' ' + className + ' ';
+
+    for (var i = 0, child, cn; child = nodes[i]; i++) {
+      if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
+          (classNames && classNames.all(function(name) {
+            return !name.toString().blank() && cn.include(' ' + name + ' ');
+          }))))
+        elements.push(Element.extend(child));
+    }
+    return elements;
+  };
+
+  return function(className, parentElement) {
+    return $(parentElement || document.body).getElementsByClassName(className);
+  };
+}(Element.Methods);
+
+/*--------------------------------------------------------------------------*/
+
+Element.ClassNames = Class.create();
+Element.ClassNames.prototype = {
+  initialize: function(element) {
+    this.element = $(element);
+  },
+
+  _each: function(iterator) {
+    this.element.className.split(/\s+/).select(function(name) {
+      return name.length > 0;
+    })._each(iterator);
+  },
+
+  set: function(className) {
+    this.element.className = className;
+  },
+
+  add: function(classNameToAdd) {
+    if (this.include(classNameToAdd)) return;
+    this.set($A(this).concat(classNameToAdd).join(' '));
+  },
+
+  remove: function(classNameToRemove) {
+    if (!this.include(classNameToRemove)) return;
+    this.set($A(this).without(classNameToRemove).join(' '));
+  },
+
+  toString: function() {
+    return $A(this).join(' ');
+  }
+};
+
+Object.extend(Element.ClassNames.prototype, Enumerable);
+
+/*--------------------------------------------------------------------------*/
+
+Element.addMethods();
\ No newline at end of file
diff --git a/js/src/revlog.js b/js/src/revlog.js
new file mode 100644
index 0000000..a912b0e
--- /dev/null
+++ b/js/src/revlog.js
@@ -0,0 +1,53 @@
+/**
+ * $Horde: chora/js/src/revlog.js,v 1.4.2.1 2008/10/09 17:40:05 jan Exp $
+ *
+ * Revision log javascript.
+ */
+
+var revlog_selected = null;
+var isMSIE = /*@cc_on!@*/false;
+
+function revlog_highlight()
+{
+    var revlog_body = $('revlog_body');
+
+    $A(revlog_body.getElementsByTagName('TR')).each(function(tr) {
+        if (isMSIE) {
+            Event.observe(tr, 'mouseover', (function() { Element.addClassName(this, 'hover'); }).bind(tr));
+            Event.observe(tr, 'mouseout', (function() { Element.removeClassName(this, 'hover'); }).bind(tr));
+        }
+        Event.observe(tr, 'click', revlog_toggle.bindAsEventListener(tr));
+    });
+}
+
+function revlog_toggle(e)
+{
+    // Ignore clicks on links.
+    var elt = Event.element(e);
+    while (elt != this) {
+        if (elt.tagName.toUpperCase() == 'A' && elt.getAttribute('href')) {
+            return;
+        }
+        elt = elt.parentNode;
+    }
+
+    if (revlog_selected != null) {
+        Element.removeClassName(revlog_selected, 'selected');
+        if (revlog_selected == this) {
+            revlog_selected = null;
+            Element.removeClassName('revlog_body', 'selection');
+            return;
+        }
+    }
+
+    revlog_selected = this;
+    Element.addClassName(this, 'selected');
+    Element.addClassName('revlog_body', 'selection');
+}
+
+function revlog_sdiff(link)
+{
+    link.href = link.href.replace(/r1=([\d\.]+)/, 'r1=' + revlog_selected.id.substring(3));
+}
+
+Event.observe(window, 'load', revlog_highlight);
diff --git a/js/src/stripe.js b/js/src/stripe.js
new file mode 100644
index 0000000..feaa123
--- /dev/null
+++ b/js/src/stripe.js
@@ -0,0 +1,43 @@
+/**
+ * Javascript code for finding all tables with classname "striped" and
+ * dynamically striping their row colors.
+ *
+ * $Horde: chora/js/src/stripe.js,v 1.4.2.1 2008/10/09 17:40:05 jan Exp $
+ *
+ * @author Chuck Hagenbuch <chuck at horde.org>
+ * @author Matt Warden <mwarden at gmail.com>
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ */
+
+function stripeAllElements()
+{
+    $$('.striped').each(stripeElement);
+}
+
+function stripeElement(elt)
+{
+    var classes = [ 'rowEven', 'rowOdd' ],
+        e = [];
+
+    if (elt.tagName == 'TABLE') {
+        // Tables can have more than one tbody element; get all child
+        // tbody tags and interate through them.
+        elt.immediateDescendants().each(stripeElement);
+        return;
+    } else {
+        // Toggle the classname of any child node that is an element.
+        e = elt.immediateDescendants();
+    }
+
+    e.each(function(c) {
+        c.removeClassName(classes[1]);
+        c.addClassName(classes[0]);
+        classes.reverse(true);
+    });
+}
+
+/* We have to wait for the full DOM to be loaded to ensure we don't
+ * miss anything. */
+document.observe('dom:loaded', stripeAllElements);
diff --git a/js/src/tables.js b/js/src/tables.js
new file mode 100644
index 0000000..f4e840b
--- /dev/null
+++ b/js/src/tables.js
@@ -0,0 +1,264 @@
+/**
+ * Javascript code for finding all tables with classname "striped" and
+ * dynamically striping their row colors, and finding all tables with
+ * classname "sortable" and making them dynamically sortable.
+ *
+ * TODO: incorporate missing features (if wanted) and improvements from:
+ * http://tetlaw.id.au/view/blog/table-sorting-with-prototype/
+ * http://www.millstream.com.au/view/code/tablekit/
+ * http://tablesorter.com/docs/
+ *
+ * $Horde: chora/js/src/tables.js,v 1.6.2.1 2008/10/09 17:40:05 jan Exp $
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ */
+
+var SORT_COLUMN_INDEX;
+
+function table_stripe(table)
+{
+    var classes = [ 'rowEven', 'rowOdd' ];
+
+    // Tables can have more than one tbody element; get all child
+    // tbody tags and interate through them.
+    table.select('tbody tr').each(function(c) {
+        c.removeClassName(classes[1]);
+        c.addClassName(classes[0]);
+        classes.reverse(true);
+    });
+}
+
+function table_makeSortable(table)
+{
+    var i = 0;
+
+    // We have a first row: assume it's the header, and make its
+    // contents clickable links.
+    table.down('tr').immediateDescendants().each(function(cell) {
+        if (cell.hasClassName('nosort')) {
+            ++i;
+            return;
+        }
+
+        cell.setAttribute('columnIndex', i++);
+        cell.setStyle({ cursor: 'pointer' });
+        cell.observe('click', function(e) {
+            var c = e.findElement('th');
+            e.stop();
+            var a = c.down('a');
+            if (a && !a.hasClassName('sortlink')) {
+                return true;
+            }
+            table_resortTable(c);
+            return false;
+        });
+    });
+}
+
+function table_getSortValue(el)
+{
+    var str = '';
+
+    if (Object.isString(el) || Object.isUndefined(el)) {
+        return el;
+    }
+
+    // Use "sortval" if defined.
+    el = $(el);
+    if (el.readAttribute('sortval')) {
+        return el.readAttribute('sortval');
+    }
+
+    if (el.innerText) {
+        // Not needed but it is faster.
+        return el.innerText;
+    }
+
+    $A(el.childNodes).each(function(e) {
+        switch (e.nodeType) {
+        case 1:
+            // ELEMENT_NODE
+            str += table_getSortValue(e);
+            break;
+
+        case 3:
+            // TEXT_NODE
+            str += e.nodeValue;
+            break;
+        }
+    });
+
+    return str;
+}
+
+function table_resortTable(th)
+{
+    var table = th.up('table'),
+        th_siblings = th.up().immediateDescendants(),
+        sortfn,
+        sortDown = 0;
+
+    th_siblings.each(function(e) {
+        if (th == e) {
+            if (e.hasClassName('sortup')) {
+                e.removeClassName('sortup');
+                e.addClassName('sortdown');
+            } else if (e.hasClassName('sortdown')) {
+                e.removeClassName('sortdown');
+                e.addClassName('sortup');
+                sortDown = 1;
+            } else {
+                e.addClassName('sortdown');
+            }
+        } else {
+            e.removeClassName('sortup');
+            e.removeClassName('sortdown');
+        }
+    });
+
+    // Work out a type for the column
+    if (th_siblings.size() <= 1) {
+        return;
+    }
+
+    SORT_COLUMN_INDEX = th.readAttribute('columnIndex');
+    var itm = table_getSortValue(table.down('tbody > tr').cells[SORT_COLUMN_INDEX]);
+
+    if (itm.match(/^\d\d[\/-]\d\d[\/-]\d\d\d\d$/) ||
+        itm.match(/^\d\d[\/-]\d\d[\/-]\d\d$/)) {
+        sortfn = table_sort_date;
+    } else if (itm.match(/^[£$]/)) {
+        sortfn = table_sort_currency;
+    } else if (itm.match(/^[\d\.]+$/)) {
+        sortfn = table_sort_numeric;
+    } else {
+        sortfn = table_sort_caseinsensitive;
+    }
+
+    // Don't mix up seperate tbodies; sort each in turn.
+    $A(table.getElementsByTagName('tbody')).each(function(tbody) {
+        var bottomRows = [ ],
+            newRows = $A(tbody.getElementsByTagName('tr'));
+
+        newRows.sort(sortfn);
+        if (sortDown) {
+            newRows.reverse();
+        }
+
+        // We appendChild rows that already exist to the tbody, so it
+        // moves them rather than creating new ones. Don't do
+        // sortbottom rows.
+        newRows.each(function(r) {
+            if (r.hasClassName('sortbottom')) {
+                bottomRows.push(r);
+            } else {
+                tbody.appendChild(r);
+            }
+        });
+
+        // Do sortbottom rows only.
+        bottomRows.each(function(r) {
+            tbody.appendChild(r);
+        });
+    });
+
+    // If we just resorted a striped table, re-stripe it.
+    if (table.hasClassName('striped')) {
+        table_stripe(table);
+    }
+
+    // Finally, see if we have a callback function to trigger.
+    if (typeof table_sortCallback != 'undefined' && Object.isFunction(table_sortCallback)) {
+        table_sortCallback(table.id, th.id, sortDown);
+    }
+}
+
+function table_sort_date(a, b)
+{
+    // Two digit years less than 50 are treated as 20XX, greater than
+    // 50 are treated as 19XX.
+    var aa = table_getSortValue(a.cells[SORT_COLUMN_INDEX]),
+        bb = table_getSortValue(b.cells[SORT_COLUMN_INDEX]),
+        dt1, dt2, yr;
+
+    if (aa.length == 10) {
+        dt1 = aa.substr(6, 4) + aa.substr(3, 2) + aa.substr(0, 2);
+    } else {
+        yr = aa.substr(6, 2);
+        if (parseInt(yr) < 50) {
+            yr = '20' + yr;
+        } else {
+            yr = '19' + yr;
+        }
+        dt1 = yr + aa.substr(3, 2) + aa.substr(0, 2);
+    }
+    if (bb.length == 10) {
+        dt2 = bb.substr(6, 4) + bb.substr(3, 2) + bb.substr(0, 2);
+    } else {
+        yr = bb.substr(6, 2);
+        if (parseInt(yr) < 50) {
+            yr = '20' + yr;
+        } else {
+            yr = '19' + yr;
+        }
+        dt2 = yr + bb.substr(3, 2) + bb.substr(0, 2);
+    }
+    if (dt1 == dt2) {
+        return 0;
+    } else if (dt1 < dt2) {
+        return -1;
+    }
+    return 1;
+}
+
+function table_sort_currency(a, b)
+{
+    var aa = table_getSortValue(a.cells[SORT_COLUMN_INDEX]).replace(/[^0-9.]/g, ''),
+        bb = table_getSortValue(b.cells[SORT_COLUMN_INDEX]).replace(/[^0-9.]/g, '');
+    return parseFloat(aa) - parseFloat(bb);
+}
+
+function table_sort_numeric(a, b)
+{
+    var aa = parseFloat(table_getSortValue(a.cells[SORT_COLUMN_INDEX]));
+    if (isNaN(aa)) {
+        aa = 0;
+    }
+    var bb = parseFloat(table_getSortValue(b.cells[SORT_COLUMN_INDEX]));
+    if (isNaN(bb)) {
+        bb = 0;
+    }
+    return aa - bb;
+}
+
+function table_sort_caseinsensitive(a, b)
+{
+    var aa = table_getSortValue(a.cells[SORT_COLUMN_INDEX]).toLowerCase(),
+        bb = table_getSortValue(b.cells[SORT_COLUMN_INDEX]).toLowerCase();
+    if (aa == bb) {
+        return 0;
+    } else if (aa < bb) {
+        return -1;
+    }
+    return 1;
+}
+
+function table_sort_default(a, b)
+{
+    var aa = table_getSortValue(a.cells[SORT_COLUMN_INDEX]),
+        bb = table_getSortValue(b.cells[SORT_COLUMN_INDEX]);
+    if (aa == bb) {
+        return 0;
+    } else if (aa < bb) {
+        return -1;
+    }
+    return 1;
+}
+
+/* We do everything onload so that the entire document is present
+ * before we start searching it for tables. */
+document.observe('dom:loaded', function() {
+    $$('table.striped').each(table_stripe);
+    $$('table.sortable').each(table_makeSortable);
+});
diff --git a/js/stripe.js b/js/stripe.js
new file mode 100644
index 0000000..f4c09bc
--- /dev/null
+++ b/js/stripe.js
@@ -0,0 +1 @@
+function stripeAllElements(){$$(".striped").each(stripeElement)}function stripeElement(A){var B=["rowEven","rowOdd"],C=[];if(A.tagName=="TABLE"){A.immediateDescendants().each(stripeElement);return }else{C=A.immediateDescendants()}C.each(function(D){D.removeClassName(B[1]);D.addClassName(B[0]);B.reverse(true)})}document.observe("dom:loaded",stripeAllElements)
\ No newline at end of file
diff --git a/js/tables.js b/js/tables.js
new file mode 100644
index 0000000..5dc3bfa
--- /dev/null
+++ b/js/tables.js
@@ -0,0 +1 @@
+var SORT_COLUMN_INDEX;function table_stripe(B){var A=["rowEven","rowOdd"];B.select("tbody tr").each(function(C){C.removeClassName(A[1]);C.addClassName(A[0]);A.reverse(true)})}function table_makeSortable(B){var A=0;B.down("tr").immediateDescendants().each(function(C){if(C.hasClassName("nosort")){++A;return }C.setAttribute("columnIndex",A++);C.setStyle({cursor:"pointer"});C.observe("click",function(E){var F=E.findElement("th");E.stop();var D=F.down("a");if(D&&!D.hasClassName("sortlink")){return true}table_resortTable(F);return false})})}function table_getSortValue(A){var B="";if(Object.isString(A)||Object.isUndefined(A)){return A}A=$(A);if(A.readAttribute("sortval")){return A.readAttribute("sortval")}if(A.innerText){return A.innerText}$A(A.childNodes).each(function(C){switch(C.nodeType){case 1:B+=table_getSortValue(C);break;case 3:B+=C.nodeValue;break}});return B}function table_resortTable(D){var C=D.up("table"),E=D.up().immediateDescendants(),B,A=0;E.each(function(G){if(D==G){if(G.hasClassName("sortup")){G.removeClassName("sortup");G.addClassName("sortdown")}else{if(G.hasClassName("sortdown")){G.removeClassName("sortdown");G.addClassName("sortup");A=1}else{G.addClassName("sortdown")}}}else{G.removeClassName("sortup");G.removeClassName("sortdown")}});if(E.size()<=1){return }SORT_COLUMN_INDEX=D.readAttribute("columnIndex");var F=table_getSortValue(C.down("tbody > tr").cells[SORT_COLUMN_INDEX]);if(F.match(/^\d\d[\/-]\d\d[\/-]\d\d\d\d$/)||F.match(/^\d\d[\/-]\d\d[\/-]\d\d$/)){B=table_sort_date}else{if(F.match(/^[?$]/)){B=table_sort_currency}else{if(F.match(/^[\d\.]+$/)){B=table_sort_numeric}else{B=table_sort_caseinsensitive}}}$A(C.getElementsByTagName("tbody")).each(function(H){var G=[],I=$A(H.getElementsByTagName("tr"));I.sort(B);if(A){I.reverse()}I.each(function(J){if(J.hasClassName("sortbottom")){G.push(J)}else{H.appendChild(J)}});G.each(function(J){H.appendChild(J)})});if(C.hasClassName("striped")){table_stripe(C)}if(typeof table_sortCallback!="undefined"&&Object.isFunction(table_sortCallback)){table_sortCallback(C.id,D.id,A)}}function table_sort_date(C,A){var F=table_getSortValue(C.cells[SORT_COLUMN_INDEX]),G=table_getSortValue(A.cells[SORT_COLUMN_INDEX]),D,B,E;if(F.length==10){D=F.substr(6,4)+F.substr(3,2)+F.substr(0,2)}else{E=F.substr(6,2);if(parseInt(E)<50){E="20"+E}else{E="19"+E}D=E+F.substr(3,2)+F.substr(0,2)}if(G.length==10){B=G.substr(6,4)+G.substr(3,2)+G.substr(0,2)}else{E=G.substr(6,2);if(parseInt(E)<50){E="20"+E}else{E="19"+E}B=E+G.substr(3,2)+G.substr(0,2)}if(D==B){return 0}else{if(D<B){return -1}}return 1}function table_sort_currency(B,A){var C=table_getSortValue(B.cells[SORT_COLUMN_INDEX]).replace(/[^0-9.]/g,""),D=table_getSortValue(A.cells[SORT_COLUMN_INDEX]).replace(/[^0-9.]/g,"");return parseFloat(C)-parseFloat(D)}function table_sort_numeric(B,A){var C=parseFloat(table_getSortValue(B.cells[SORT_COLUMN_INDEX]));if(isNaN(C)){C=0}var D=parseFloat(table_getSortValue(A.cells[SORT_COLUMN_INDEX]));if(isNaN(D)){D=0}return C-D}function table_sort_caseinsensitive(B,A){var C=table_getSortValue(B.cells[SORT_COLUMN_INDEX]).toLowerCase(),D=table_getSortValue(A.cells[SORT_COLUMN_INDEX]).toLowerCase();if(C==D){return 0}else{if(C<D){return -1}}return 1}function table_sort_default(B,A){var C=table_getSortValue(B.cells[SORT_COLUMN_INDEX]),D=table_getSortValue(A.cells[SORT_COLUMN_INDEX]);if(C==D){return 0}else{if(C<D){return -1}}return 1}document.observe("dom:loaded",function(){$$("table.striped").each(table_stripe);$$("table.sortable").each(table_makeSortable)})
\ No newline at end of file
diff --git a/lib/Block/tree_menu.php b/lib/Block/tree_menu.php
new file mode 100644
index 0000000..a221961
--- /dev/null
+++ b/lib/Block/tree_menu.php
@@ -0,0 +1,40 @@
+<?php
+
+$block_name = _("Menu List");
+$block_type = 'tree';
+
+/**
+ * $Horde: chora/lib/Block/tree_menu.php,v 1.5.2.1 2008/10/09 17:40:05 jan Exp $
+ */
+class Horde_Block_chora_tree_menu extends Horde_Block {
+
+    var $_app = 'chora';
+
+    function _buildTree(&$tree, $indent = 0, $parent = null)
+    {
+        global $perms, $sourceroots;
+
+        define('CHORA_ERROR_HANDLER', true);
+        require_once dirname(__FILE__) . '/../base.php';
+
+        $arr = array();
+        asort($sourceroots);
+        foreach ($sourceroots as $key => $val) {
+            if ((!$perms->exists('chora:sourceroots:' . $key) ||
+                 $perms->hasPermission('chora:sourceroots:' . $key,
+                                       Auth::getAuth(),
+                                       PERMS_READ | PERMS_SHOW))) {
+                $tree->addNode($parent . $key,
+                               $parent,
+                               $val['name'],
+                               $indent + 1,
+                               false,
+                               array('icon' => 'folder.png',
+                                     'icondir' => $registry->getImageDir('horde') . '/tree',
+                                     'url' => Chora::url('', '', array('rt' => $key))));
+            }
+        }
+
+    }
+
+}
diff --git a/lib/Chora.php b/lib/Chora.php
index bfc70f2..e34bc44 100644
--- a/lib/Chora.php
+++ b/lib/Chora.php
@@ -2,7 +2,7 @@
 /**
  * Chora Base Class.
  *
- * $Horde: chora/lib/Chora.php,v 1.72.6.7 2007/02/23 03:58:53 selsky Exp $
+ * $Horde: chora/lib/Chora.php,v 1.72.6.8 2008/10/09 17:40:05 jan Exp $
  *
  * @author  Anil Madhavapeddy <avsm at horde.org>
  * @package Chora
@@ -64,11 +64,12 @@ class Chora {
     /**
      * Initialize global variables and objects.
      */
-    function init()
+    function initialize()
     {
         global $acts, $defaultActs, $conf, $where, $atdir,
-            $fullname, $prefs, $sourceroots, $sourceroot, $scriptName;
+            $fullname, $prefs, $sourceroot, $scriptName;
 
+        $sourceroots = Chora::sourceroots();
         /**
          * Variables we wish to propagate across web pages
          *  sbt = Sort By Type (name, age, author, etc)
@@ -111,7 +112,7 @@ class Chora {
         }
 
         if (!isset($sourceroots[$acts['rt']])) {
-            Chora::fatal('Malformed URL');
+            Chora::fatal(404, 'Malformed URL');
         }
 
         $sourcerootopts = $sourceroots[$acts['rt']];
@@ -120,22 +121,17 @@ class Chora {
         $conf['paths']['temp'] = Horde::getTempDir();
         $GLOBALS['VC'] = &VC::factory($sourcerootopts['type'],
                                       array('sourceroot' => $sourcerootopts['location'],
-                                            'paths' => $conf['paths']));
+                                            'paths' => $conf['paths'],
+                                            'username' => isset($sourcerootopts['username']) ? $sourcerootopts['username'] : '',
+                                            'password' => isset($sourcerootopts['password']) ? $sourcerootopts['password'] : ''));
 
         $conf['paths']['sourceroot'] = $sourcerootopts['location'];
-        $conf['paths']['cvsusers'] = $sourcerootopts['location'] . '/' . @$sourcerootopts['cvsusers'];
-        $conf['paths']['introText'] = CHORA_BASE . '/config/' . @$sourcerootopts['intro'];
-        $conf['options']['introTitle'] = @$sourcerootopts['title'];
+        $conf['paths']['cvsusers'] = $sourcerootopts['location'] . '/' . (isset($sourcerootopts['cvsusers']) ? $sourcerootopts['cvsusers'] : '');
+        $conf['paths']['introText'] = CHORA_BASE . '/config/' . (isset($sourcerootopts['intro']) ? $sourcerootopts['intro'] : '');
+        $conf['options']['introTitle'] = isset($sourcerootopts['title']) ? $sourcerootopts['title'] : '';
         $conf['options']['sourceRootName'] = $sourcerootopts['name'];
 
         $where = Util::getFormData('f', '');
-
-        /* Override $where with PATH_INFO if appropriate. */
-        if (($conf['options']['urls'] == 'path_info' || $conf['options']['urls'] == 'rewrite') &&
-            isset($_SERVER['PATH_INFO'])) {
-            $where = $_SERVER['PATH_INFO'];
-        }
-
         if ($where == '') {
             $where = '/';
         }
@@ -170,6 +166,7 @@ class Chora {
         }
 
         $fullname = $sourcerootopts['location'] . (substr($sourcerootopts['location'], -1) == '/' ? '' : '/') . $where;
+
         if ($sourcerootopts['type'] == 'cvs') {
             $fullname = preg_replace('|/$|', '', $fullname);
             $atdir = @is_dir($fullname);
@@ -179,7 +176,11 @@ class Chora {
         $where = preg_replace('|/$|', '', $where);
 
         if ($sourcerootopts['type'] == 'cvs' && !@is_dir($sourcerootopts['location'])) {
-            Chora::fatal(_("SourceRoot not found! This could be a misconfiguration by the server administrator, or the server could be having temporary problems. Please try again later."));
+            Chora::fatal('500 Internal Server Error', _("SourceRoot not found! This could be a misconfiguration by the server administrator, or the server could be having temporary problems. Please try again later."));
+        }
+
+        if (Chora::isRestricted($where)) {
+            Chora::fatal('403 Forbidden', "$where: Forbidden by server configuration");
         }
     }
 
@@ -210,16 +211,21 @@ class Chora {
     /**
      * Output an error page.
      *
-     * @param string $msg  The verbose error message to be displayed.
+     * @param string $errcode  The HTTP error number and text.
+     * @param string $errmsg   The verbose error message to be displayed.
      */
-    function fatal($msg)
+    function fatal($errcode, $errmsg)
     {
+        if (defined('CHORA_ERROR_HANDLER') && constant('CHORA_ERROR_HANDLER')) {
+            return;
+        }
+
         global $registry, $conf, $notification, $browser, $prefs;
 
         /* Don't store the bad file in the user's preferences. */
         $prefs->setValue('last_file', '');
 
-        $notification->push($msg, 'horde.error');
+        $notification->push($errcode . ': ' . $errmsg, 'horde.error');
         require CHORA_TEMPLATES . '/common-header.inc';
         require CHORA_TEMPLATES . '/menu.inc';
         require $registry->get('templates', 'horde') . '/common-footer.inc';
@@ -235,51 +241,11 @@ class Chora {
     function checkError($e)
     {
         if (is_a($e, 'PEAR_Error')) {
-            Chora::fatal($e->getMessage());
+            Chora::fatal($e->getCode(), $e->getMessage());
         }
     }
 
     /**
-     * Return an array with the names of any of the variables we need
-     * to keep, that are different from the defaults.
-     *
-     * @return array  Names/vals of differing variables.
-     */
-    function differingVars()
-    {
-        global $acts, $defaultActs;
-        reset($acts);
-        $ret = array();
-        foreach ($acts as $key => $val) {
-            if ($val != $defaultActs[$key]) {
-                $ret[$key] = $val;
-            }
-        }
-        return $ret;
-    }
-
-    /**
-     * Generate a series of hidden input variables based on the GET
-     * parameters which are different from the defaults.
-     *
-     * @param array $except  Array of exceptions to never output.
-     *
-     * @return string  A set of input tags with the different variables.
-     */
-    function generateHiddens($except = array())
-    {
-        global $acts;
-        $toOut = Chora::differingVars();
-        $ret = Util::formInput() . "\n";
-        while (list($key, $val) = each($toOut)) {
-            if (is_array($except) && !in_array($key, $except)) {
-                $ret .= "<input type=\"hidden\" name=\"$key\" value=\"$val\" />\n";
-            }
-        }
-        return $ret;
-    }
-
-    /**
      * Convert a commit-name into whatever the user wants.
      *
      * @param string $name  Account name.
@@ -288,23 +254,24 @@ class Chora {
      */
     function showAuthorName($name, $fullname = false)
     {
-        global $VC;
+        static $users = null;
+        if ($users === null) {
+            $users = $GLOBALS['VC']->getUsers($GLOBALS['conf']['paths']['cvsusers']);
+        }
 
-        $users = $VC->getUsers($GLOBALS['conf']['paths']['cvsusers']);
         if (is_array($users) && isset($users[$name])) {
-            return '<a href="mailto:' . $users[$name]['mail'] . '">' .
-                ($fullname ? $users[$name]['name'] : $name) .
-                '</a>' . ($fullname ? " <i>($name)</i>" : '');
-        } else {
-            return $name;
+            return '<a href="mailto:' . htmlspecialchars($users[$name]['mail']) . '">' .
+                htmlspecialchars($fullname ? $users[$name]['name'] : $name) .
+                '</a>' . ($fullname ? ' <em>' . htmlspecialchars($name) . '</em>' : '');
         }
+        return htmlspecialchars($name);
     }
 
     /**
      * Generate a URL that links into Chora.
      *
      * @param string $script  Name of the Chora script to link into
-     * @param string $uri     Any PATH_INFO portion that should be included
+     * @param string $uri     The path being browsed.
      * @param array  $args    Key/value pair of any GET parameters to append
      * @param string $anchor  Anchor entity name
      *
@@ -312,33 +279,32 @@ class Chora {
      */
     function url($script = '', $uri = '', $args = array(), $anchor = '')
     {
-        global $conf;
-
-        $arglist = array_merge(Chora::differingVars(), $args);
-        $script = $script ? $script . '.php' : '';
+        global $conf, $acts, $defaultActs;
 
-        if ($conf['options']['urls'] != 'get') {
-            if ($conf['options']['urls'] == 'path_info' && empty($script)) {
-                $script = 'browse.php';
+        $differing = array();
+        foreach ($acts as $key => $val) {
+            if ($val != $defaultActs[$key]) {
+                $differing[$key] = $val;
             }
+        }
 
-            if (substr($uri, 0, 1) != '/' && $script) {
-                $script .= '/';
-            }
-            $script .= $uri;
-            if (substr($script, 0, 1) == '/') {
-                $script = substr($script, 1);
+        $arglist = array_merge($differing, $args);
+        $script = $script ? $script . '.php' : 'browse.php';
+
+        if ($conf['options']['urls'] == 'rewrite') {
+            if ($script == 'browse.php') {
+                $script = $uri;
+                if (substr($script, 0, 1) == '/') {
+                    $script = substr($script, 1);
+                }
+            } else {
+                $script .= '/' . $uri;
             }
         } else {
             $arglist['f'] = $uri;
-            if (empty($script)) {
-                $script = 'browse.php';
-            }
         }
 
-        $url = Horde::applicationUrl($script);
-        $url = Util::addParameter($url, $arglist);
-
+        $url = Util::addParameter(Horde::applicationUrl($script), $arglist);
         if (!empty($anchor)) {
             $url .= "#$anchor";
         }
@@ -347,6 +313,57 @@ class Chora {
     }
 
     /**
+     * Generates hidden form fields with all required parameters.
+     *
+     * @param array  $args    Key/value pair of any POST parameters to append
+     *
+     * @return string  The form fields, with session information if necessary.
+     */
+    function formInputs($args = array())
+    {
+        global $conf, $acts, $defaultActs;
+
+        $differing = array();
+        foreach ($acts as $key => $val) {
+            if ($val != $defaultActs[$key]) {
+                $differing[$key] = $val;
+            }
+        }
+
+        $arglist = array_merge($differing, $args);
+
+        $fields = Util::formInput();
+        foreach ($arglist as $key => $val) {
+            $fields .= '<input type="hidden" name="' . htmlspecialchars($key)
+                . '" value="' . htmlspecialchars($val) . '" />';
+        }
+
+        return $fields;
+    }
+
+    /**
+     * Returns the entries of $sourceroots that the current user has access to.
+     *
+     * @return array  The sourceroots that the current user has access to.
+     */
+    function sourceroots()
+    {
+        global $perms, $sourceroot, $sourceroots;
+
+        $arr = array();
+        foreach ($sourceroots as $key => $val) {
+            if (!$perms->exists('chora:sourceroots:' . $key) ||
+                 $perms->hasPermission('chora:sourceroots:' . $key,
+                                       Auth::getAuth(),
+                                       PERMS_READ | PERMS_SHOW)) {
+                $arr[$key] = $val;
+            }
+        }
+
+        return $arr;
+    }
+
+    /**
      * Generate a list of repositories available from this
      * installation of Chora.
      *
@@ -354,21 +371,26 @@ class Chora {
      */
     function repositories()
     {
-        global $sourceroot, $sourceroots, $defaultActs;
+        $sourceroots = Chora::sourceroots();
+        $num_repositories = count($sourceroots);
+        if ($num_repositories == 1) {
+            return '';
+        }
 
         $arr = array();
         foreach ($sourceroots as $key => $val) {
-            if ($sourceroot != $key) {
-                $arr[] = '<b><a href="' . Chora::url('', '', array('rt' => $key)) . '">' .
-                    $val['name'] . '</a></b>';
+            if ($GLOBALS['sourceroot'] != $key) {
+                $arr[] = '<option value="' .
+                    Chora::url('', '', array('rt' => $key)) .
+                    '">' . $val['name'] . '</option>';
             }
         }
 
-        if (count($arr)) {
-            return _("Other Repositories") . ': ' . implode(' , ', $arr);
-        } else {
-            return '';
-        }
+        return
+            '<form action="#" id="repository-picker">'
+            . '<select onchange="location.href=this[this.selectedIndex].value">'
+            . '<option value="">' . _("Change repositories:") . '</option>'
+            . implode(' , ', $arr) . '</select></form>';
     }
 
     /**
@@ -398,9 +420,17 @@ class Chora {
      **/
     function isRestricted($item)
     {
-        global $conf, $sourceroots, $sourceroot;
+        global $conf, $perms, $sourceroots, $sourceroot;
         static $restricted;
 
+        // First check if the current user has access to this repository.
+        if ($perms->exists('chora:sourceroots:' . $sourceroot) &&
+            !$perms->hasPermission('chora:sourceroots:' . $sourceroot,
+                                   Auth::getAuth(),
+                                   PERMS_READ | PERMS_SHOW)) {
+            return true;
+        }
+
         if (!isset($restricted)) {
             $restricted = array();
             if (isset($conf['restrictions']) && is_array($conf['restrictions'])) {
@@ -445,33 +475,50 @@ class Chora {
         }
     }
 
+    /**
+     */
     function getFileViews()
     {
         global $where;
 
         $views = array();
-        $current = $_SERVER['PHP_SELF'];
-        if (!empty($_SERVER['PATH_INFO'])) {
-            $current = str_replace($_SERVER['PATH_INFO'], '', $current);
-        }
-        $current = str_replace('.php', '', basename($current));
+        $current = str_replace('.php', '', basename($_SERVER['PHP_SELF']));
 
-        $views[] = $current == 'browse' ? '<i class="widget">' . _("Logs") . '</i>' : Horde::widget(Chora::url('', $where), _("Logs"), 'widget', '', '', _("_Logs"));
+        $views[] = $current == 'browse'
+            ? '<em class="widget">' . _("Logs") . '</em>'
+            : Horde::widget(Chora::url('', $where), _("Logs"), 'widget', '',
+                            '', _("_Logs"));
         // Subversion supports patchsets natively.
-        if (!empty($GLOBALS['conf']['paths']['cvsps']) || is_a($GLOBALS['VC'], 'VC_svn')) {
-            $views[] = $current == 'patchsets' ? '<i class="widget">' . _("Patchsets") . '</i>' : Horde::widget(Chora::url('patchsets', $where), _("Patchsets"), 'widget', '', '', _("_Patchsets"));
+        if (!empty($GLOBALS['conf']['paths']['cvsps']) ||
+            is_a($GLOBALS['VC'], 'VC_svn')) {
+            $views[] = $current == 'patchsets'
+                ? '<em class="widget">' . _("Patchsets") . '</em>'
+                : Horde::widget(Chora::url('patchsets', $where), _("Patchsets"),
+                                'widget', '', '', _("_Patchsets"));
         }
         if (!is_a($GLOBALS['VC'], 'VC_svn')) {
-            $views[] = $current == 'history' ? '<i class="widget">' . _("Branches") . '</i>' : Horde::widget(Chora::url('history', $where), _("Branches"), 'widget', '', '', _("_Branches"));
-        }
-        if (!empty($GLOBALS['conf']['paths']['cvsgraph']) && !is_a($GLOBALS['VC'], 'VC_svn')) {
-            $views[] = $current == 'cvsgraph' ? '<i class="widget">' . _("Graph") . '</i>' : Horde::widget(Chora::url('cvsgraph', $where), _("Graph"), 'widget', '', '', _("_Graph"));
+            if (empty($GLOBALS['conf']['paths']['cvsgraph'])) {
+                $views[] = $current == 'history'
+                    ? '<em class="widget">' . _("Branches") . '</em>'
+                    : Horde::widget(Chora::url('history', $where), _("Branches"),
+                                    'widget', '', '', _("_Branches"));
+            } else {
+                $views[] = $current == 'cvsgraph'
+                    ? '<em class="widget">' . _("Branches") . '</em>'
+                    : Horde::widget(Chora::url('cvsgraph', $where), _("Branches"),
+                                    'widget', '', '', _("_Branches"));
+            }
         }
-        $views[] = $current == 'stats' ? '<i class="widget">' . _("Statistics") . '</i>' : Horde::widget(Chora::url('stats', $where), _("Statistics"), 'widget', '', '', _("_Statistics"));
+        $views[] = $current == 'stats'
+            ? '<em class="widget">' . _("Statistics") . '</em>'
+            : Horde::widget(Chora::url('stats', $where), _("Statistics"),
+                            'widget', '', '', _("_Statistics"));
 
         return _("View:") . ' ' . implode(' | ', $views);
     }
 
+    /**
+     */
     function formatLogMessage($log)
     {
         global $conf;
@@ -487,4 +534,109 @@ class Chora {
         return $log;
     }
 
+    /**
+     * Return a list of tags for a given log entry.
+     *
+     * @since Chora 2.1
+     *
+     * @param VC_Log_* $lg   The VC_Log object.
+     * @param string $where  The filename.
+     *
+     * @return array  An array of linked tags.
+     */
+    function getTags($lg, $where)
+    {
+        $tags = array();
+        foreach ($lg->querySymbolicBranches() as $symb => $bra) {
+            $tags[] = '<a href="' . Chora::url('', $where, array('onb' => $bra)) . '">'. htmlspecialchars($symb) . '</a>';
+        }
+        if ($lg->tags) {
+            foreach ($lg->tags as $tag) {
+            $tags[] = htmlspecialchars($tag);
+            }
+        }
+        return $tags;
+    }
+
+    /**
+     * Return branch information for a given revision.
+     *
+     * @since Chora 2.1
+     *
+     * @param VC_File $fl  The VC_File object.
+     * @param string $rev  The filename.
+     *
+     * @return array  An 2-member array - branch name and branch revision.
+     */
+    function getBranch($fl, $rev)
+    {
+        $branchName = '';
+        $branchRev = VC_Revision::strip($rev, 1);
+        if (isset($fl->branches[$rev])) {
+            $branchName = $fl->branches[$rev];
+        } elseif (isset($fl->branches[$branchRev])) {
+            $branchName = $fl->branches[$branchRev];
+        }
+        return array($branchName, $branchRev);
+    }
+
+    /**
+     * Create a range of revisions between two revision numbers.
+     *
+     * @since Chora 2.1
+     *
+     * @param string $r1  The initial revision.
+     * @param string $r2  The ending revision.
+     *
+     * @return array  The revision range, or empty if there is no straight
+     *                line path between the revisions.
+     */
+    function getRevisionRange($r1, $r2)
+    {
+        if (VC_Revision::cmp($r1, $r2) == 1) {
+            $curr = VC_Revision::prev($r1);
+            $stop = VC_Revision::prev($r2);
+            $flip = true;
+        } else {
+            $curr = $r2;
+            $stop = $r1;
+            $flip = false;
+        }
+
+        $ret_array = array();
+
+        do {
+            $ret_array[] = $curr;
+            $curr = VC_Revision::prev($curr);
+            if ($curr == $stop) {
+                return ($flip) ? array_reverse($ret_array) : $ret_array;
+            }
+        } while (VC_Revision::cmp($curr, $stop) != -1);
+
+        return array();
+    }
+
+    /**
+     * Return formatted date information.
+     *
+     * @since Chora 2.1
+     *
+     * @param integer $date  Number of seconds since epoch we wish to display.
+     *
+     * @return string  The date formatted pursuant to Horde prefs.
+     */
+    function formatDate($date)
+    {
+        static $format;
+
+        if (!isset($format)) {
+            $format = $GLOBALS['prefs']->getValue('date_format') .
+                ($GLOBALS['prefs']->getValue('twenty_four')
+                 ? ' %H:%M'
+                 : ' %I:%M %p');
+        }
+
+        return strftime($format, $date);
+    }
+
 }
diff --git a/lib/api.php b/lib/api.php
new file mode 100644
index 0000000..42cc2a2
--- /dev/null
+++ b/lib/api.php
@@ -0,0 +1,40 @@
+<?php
+/**
+ * Chora external API interface.
+ *
+ * $Horde: chora/lib/api.php,v 1.1.2.1 2008/10/09 17:40:05 jan Exp $
+ *
+ * This file defines Chora's external API interface. Other applications can
+ * interact with Chora through this API.
+ *
+ * @package Chora
+ */
+ at define('CHORA_BASE', dirname(__FILE__) . "/..");
+
+$_services['perms'] = array(
+    'args' => array(),
+    'type' => '{urn:horde}stringArray'
+);
+
+function _chora_perms()
+{
+    static $perms = array();
+    if (!empty($perms)) {
+        return $perms;
+    }
+
+    @define('CHORA_BASE', dirname(__FILE__) . '/..');
+    require_once CHORA_BASE . '/config/sourceroots.php';
+
+    $perms['tree']['chora']['sourceroots'] = false;
+    $perms['title']['chora:sourceroots'] = _("Repositories");
+
+    // Run through every source repository
+    foreach ($sourceroots as $sourceroot => $srconfig) {
+        $perms['tree']['chora']['sourceroots'][$sourceroot] = false;
+        $perms['title']['chora:sourceroots:' . $sourceroot] = $srconfig['name'];
+    }
+
+    return $perms;
+}
+
diff --git a/lib/base.php b/lib/base.php
index d59a3cb..7e3e840 100644
--- a/lib/base.php
+++ b/lib/base.php
@@ -2,7 +2,7 @@
 /**
  * Chora base inclusion file.
  *
- * $Horde: chora/lib/base.php,v 1.101.10.1 2007/02/23 03:58:53 selsky Exp $
+ * $Horde: chora/lib/base.php,v 1.101.10.2 2008/10/09 17:40:05 jan Exp $
  *
  * This file brings in all of the dependencies that every Chora script
  * will need, and sets up objects that all scripts use.
@@ -11,7 +11,7 @@
 // Check for a prior definition of HORDE_BASE (perhaps by an
 // auto_prepend_file definition for site customization).
 if (!defined('HORDE_BASE')) {
-    @define('HORDE_BASE', dirname(__FILE__) . '/../..');
+    define('HORDE_BASE', dirname(__FILE__) . '/../..');
 }
 
 // Load the Horde Framework core, and set up inclusion paths.
@@ -21,39 +21,41 @@ require_once HORDE_BASE . '/lib/core.php';
 $registry = &Registry::singleton();
 if (is_a(($pushed = $registry->pushApp('chora', !defined('AUTH_HANDLER'))), 'PEAR_Error')) {
     if ($pushed->getCode() == 'permission_denied') {
-        Horde::authenticationFailureRedirect(); 
+        Horde::authenticationFailureRedirect();
     }
     Horde::fatal($pushed, __FILE__, __LINE__, false);
 }
 $conf = &$GLOBALS['conf'];
- at define('CHORA_TEMPLATES', $registry->get('templates'));
+define('CHORA_TEMPLATES', $registry->get('templates'));
 
 // Notification system.
 $notification = &Notification::singleton();
 $notification->attach('status');
 
-// Cache
+// Cache.
 require_once 'Horde/Cache.php';
 $cache = &Horde_Cache::singleton($conf['cache']['driver'], Horde::getDriverConfig('cache', $conf['cache']['driver']));
 
 // Find the base file path of Chora.
- at define('CHORA_BASE', dirname(__FILE__) . '/..');
+if (!defined('CHORA_BASE')) {
+    define('CHORA_BASE', dirname(__FILE__) . '/..');
+}
 
 // Horde base libraries.
 require_once 'Horde/Text.php';
 require_once 'Horde/Help.php';
 
 // Chora libraries and config.
-require_once CHORA_BASE . '/config/sourceroots.php';
+if (is_callable(array('Horde', 'loadConfiguration'))) {
+    $sourceroots = Horde::loadConfiguration('sourceroots.php', 'sourceroots');
+} else {
+    require_once CHORA_BASE . '/config/sourceroots.php';
+}
 require_once CHORA_BASE . '/lib/Chora.php';
 require_once 'Horde/VC.php';
 
 // Initialize objects, path, etc.
-Chora::init();
-
-if (Chora::isRestricted($where)) {
-    Chora::fatal("$where: Forbidden by server configuration");
-}
+Chora::initialize();
 
-/* Start compression, if requested. */
+// Start compression, if requested.
 Horde::compressOutput();
diff --git a/lib/version.php b/lib/version.php
index 8bf5459..9077d70 100644
--- a/lib/version.php
+++ b/lib/version.php
@@ -1 +1 @@
-<?php define('CHORA_VERSION', 'H3 (2.0.2)') ?>
+<?php define('CHORA_VERSION', 'H3 (2.1)') ?>
diff --git a/locale/cs_CZ/LC_MESSAGES/chora.mo b/locale/cs_CZ/LC_MESSAGES/chora.mo
index 22794c1..2bebf51 100644
Binary files a/locale/cs_CZ/LC_MESSAGES/chora.mo and b/locale/cs_CZ/LC_MESSAGES/chora.mo differ
diff --git a/locale/da_DK/LC_MESSAGES/chora.mo b/locale/da_DK/LC_MESSAGES/chora.mo
index 61d71b0..fd499fd 100644
Binary files a/locale/da_DK/LC_MESSAGES/chora.mo and b/locale/da_DK/LC_MESSAGES/chora.mo differ
diff --git a/locale/de_DE/LC_MESSAGES/chora.mo b/locale/de_DE/LC_MESSAGES/chora.mo
index 22656fe..c5d2ff1 100644
Binary files a/locale/de_DE/LC_MESSAGES/chora.mo and b/locale/de_DE/LC_MESSAGES/chora.mo differ
diff --git a/locale/de_DE/help.xml b/locale/de_DE/help.xml
index fd4fec8..bdd2e04 100644
--- a/locale/de_DE/help.xml
+++ b/locale/de_DE/help.xml
@@ -2,36 +2,36 @@
 <help>
 
   <entry id="Overview" md5="6c904e7e17dea0fc75802e2c1a534b03" state="uptodate">
-    <title>Chora Ãœberblick</title>
+    <title>Chora Überblick</title>
     <para>
       Bei Chora handelt es sich um ein Werkzeug zum Betrachten von
       Repositories, die durch Systeme zur Versionskontrolle verwaltet
-      werden. Es stellt verschiedene Ansichten für Verzeichnisübersichten, die
+      werden. Es stellt verschiedene Ansichten für Verzeichnisübersichten, die
       Log-Geschichte, Unterschiede zwischen Versionen, grafische
-      Branchdarstellungen und kommentierte Dateien zur Verfügung.
+      Branchdarstellungen und kommentierte Dateien zur Verfügung.
     </para>
   </entry>
 
   <entry id="chora-button" md5="23ab7e475dc04b66c91b98f6b0f1d743" state="uptodate">
-    <title>Menü: Liste</title>
+    <title>Menü: Liste</title>
     <para>
       Mit dem Liste-Symbol gelangen Sie zur Hauptansicht
-      (Verzeichnisübersicht) des Standard-Repositories zurück.
+      (Verzeichnisübersicht) des Standard-Repositories zurück.
     </para>
   </entry>
 
   <entry id="options-button" md5="51f095602bb3e62703e72e7fe7fef996" state="uptodate">
-    <title>Menü: Einstellungen</title>
+    <title>Menü: Einstellungen</title>
     <para>
-      Über das Einstellungen-Symbol gelangt man zu den persönlichen
-      Einstellungen, die festlegen, wie sich Chora verhält.
+      Über das Einstellungen-Symbol gelangt man zu den persönlichen
+      Einstellungen, die festlegen, wie sich Chora verhält.
     </para>
   </entry>
 
   <entry id="help-button" md5="11b68a5e1f6cb8f5c2a885c2d65db322" state="uptodate">
-    <title>Menü: Hilfe</title>
+    <title>Menü: Hilfe</title>
     <para>
-      Das Hilfe-Symbol öffnet diese Hilfe.
+      Das Hilfe-Symbol öffnet diese Hilfe.
     </para>
   </entry>
 
@@ -47,7 +47,7 @@
     <title>Kommentierte Ansicht</title>
     <para>
       Die Kommentierte Ansicht zeigt den Inhalt einer Datei zusammen mit den
-      Namen der Autoren, die für die einzelnen Abschnitte verantwortlich sind.
+      Namen der Autoren, die für die einzelnen Abschnitte verantwortlich sind.
     </para>
   </entry>
 
@@ -64,7 +64,7 @@
     <title>Log-Ansicht</title>
     <para>
       Die Log-Ansicht zeigt die Geschichte einer Datei, indem alle
-      Log-Einträge aufgelistet werden, wie sie beim Einchecken der Versionen
+      Log-Einträge aufgelistet werden, wie sie beim Einchecken der Versionen
       angegeben wurden.
     </para>
   </entry>
@@ -73,22 +73,22 @@
     <title>Ansicht der Unterschiede</title>
     <para>
       Die Ansicht der Unterschiede zeigt die Unterschiede zwischen zwei
-      Versionen einer Datei an. So lässt sich leicht erkennen, wie eine Datei
-      verändert wurde. Es stehen verschiedene Anzeigeformate zur Verfügung.
+      Versionen einer Datei an. So lässt sich leicht erkennen, wie eine Datei
+      verändert wurde. Es stehen verschiedene Anzeigeformate zur Verfügung.
     </para>
   </entry>
 
   <entry id="show-deleted-files" md5="96c17a1efe41cd0e1de5c4f37d1e68f2" state="uptodate">
-    <title>Gelöschte Dateien anzeigen/verstecken</title>
+    <title>Gelöschte Dateien anzeigen/verstecken</title>
     <para>
-      Sie können auswählen, ob gelöschte Dateien (aus dem Attic) in der
+      Sie können auswählen, ob gelöschte Dateien (aus dem Attic) in der
       Verzeichnisansicht angezeigt oder versteckt werden sollen. Wenn Sie sich
-      gelöschte Dateien anzeigen lassen, werden die entsprechenden Zeilen in
+      gelöschte Dateien anzeigen lassen, werden die entsprechenden Zeilen in
       einer anderen Darstellung und mit einem <i>Papierkorb</i>-Symbol
       angezeigt.
     </para>
     <para>
-      Standardmäßig werden gelöschte Dateien nicht angezeigt.
+      Standardmäßig werden gelöschte Dateien nicht angezeigt.
     </para>
   </entry>
 
diff --git a/locale/es_ES/LC_MESSAGES/chora.mo b/locale/es_ES/LC_MESSAGES/chora.mo
index 38f66d5..736761e 100644
Binary files a/locale/es_ES/LC_MESSAGES/chora.mo and b/locale/es_ES/LC_MESSAGES/chora.mo differ
diff --git a/locale/es_ES/help.xml b/locale/es_ES/help.xml
index 8427eca..504b1b4 100644
--- a/locale/es_ES/help.xml
+++ b/locale/es_ES/help.xml
@@ -1,241 +1,54 @@
 <?xml version="1.0"?>
-<!-- $Horde: chora/locale/es_ES/help.xml,v 1.3 2004/12/18 16:01:17 jan Exp $ -->
+<!-- $Horde: chora/locale/es_ES/help.xml,v 1.3.2.2 2008/10/09 17:40:07 jan Exp $ -->
 <help>
-  <entry id="Overview" md5="45e4ecb52bfdcdda3d89ae65e16a99a8" state="changed">
-    <title>Introducci&#xF3;n a Chora</title>
-    <heading>Introducci&#xF3;n</heading>
-    <para>
-    Chora es una herramienta para examinar depositos de c&#xF3;digo gestionados mediante el sistema de control de c&#xF3;digo CVS. Pretende ofrecer un alto grado de integraci&#xF3;n con CVS y el resto de las herramientas basadas en web de Horde. Ofrece vista de directorio (archivos), historial del registro, diferencias entre versiones, vista ramificada y anotaciones.
-    </para>
-<!-- English entry:
-<entry id="Overview">
-    <title>Chora Overview</title>
-    <para>
-      Chora is a tool for viewing code repositories that are managed using
-      source control systems. It provides a directory (file) view, a log
-      history view, difference views between versions, a visual branch view,
-      and an annotated view.
-    </para>
-  </entry>--><!-- English entry:
-<entry id="Overview">
-    <title>Chora Overview</title>
-    <para>
-      Chora is a tool for viewing code repositories that are managed using
-      source control systems. It provides a directory (file) view, a log
-      history view, difference views between versions, a visual branch view,
-      and an annotated view.
-    </para>
-  </entry>--></entry>
-  <entry id="chora-button" md5="e52195deacd47a4a6172b8150ea30dc4" state="changed">
-    <title>Botones del men&#xFA;: Examinar</title>
-    <heading>Examinar</heading>
-    <para>
-      El bot&#xF3;n Examinar le devuelve a la visualizaci&#xF3;n principal de archivos de Chora del dep&#xF3;sito por omisi&#xF3;n.
-    </para>
-<!-- English entry:
-<entry id="chora-button">
-    <title>Menu Buttons: Browse</title>
-    <para>
-      The Browse button takes you back to the main Chora files view for the
-      default repository.
-    </para>
-  </entry>--><!-- English entry:
-<entry id="chora-button">
-    <title>Menu Buttons: Browse</title>
-    <para>
-      The Browse button takes you back to the main Chora files view for the
-      default repository.
-    </para>
-  </entry>--></entry>
-  <entry id="options-button" md5="66b35eace503e7591c0c548830f9bc4d" state="changed">
-    <title>Botones del men&#xFA;: Opciones</title>
-    <heading>Opciones</heading>
-    <para>
-    El bot&#xF3;n Opciones le permite configurar varias preferencias personales para controlar el comportamiento de Chora.
-    </para>
-<!-- English entry:
-<entry id="options-button">
-    <title>Menu Buttons: Options</title>
-    <para>
-      The Options button lets you set some personal preferences to control the
-      way Chora behaves.
-    </para>
-  </entry>--><!-- English entry:
-<entry id="options-button">
-    <title>Menu Buttons: Options</title>
-    <para>
-      The Options button lets you set some personal preferences to control the
-      way Chora behaves.
-    </para>
-  </entry>--></entry>
-  <entry id="help-button" md5="407323d4c4a4a20eda2df95607087e2c" state="changed">
-    <title>Botones del men&#xFA;: Ayuda</title>
-    <heading>Ayuda</heading>
-    <para>
-      El bot&#xF3;n Ayuda le muestra esta pantalla de ayuda.
-    </para>
-<!-- English entry:
-<entry id="help-button">
-    <title>Menu Buttons: Help</title>
-    <para>
-      The Help button brings up this help display.
-    </para>
-  </entry>--><!-- English entry:
-<entry id="help-button">
-    <title>Menu Buttons: Help</title>
-    <para>
-      The Help button brings up this help display.
-    </para>
-  </entry>--></entry>
-  <entry id="view-checkout" md5="c15ba2379a7ceba3e61fb0733c97b788" state="changed">
-    <title>Vista de inspecci&#xF3;n CVS</title>
-    <heading>Vista de inspecci&#xF3;n CVS</heading>
-    <para>
-      La vista de inspecci&#xF3;n CVS muestra el contenido de un archivo en un punto de revisi&#xF3;n, junto con las entradas de registro de dicha revisi&#xF3;n.
-    </para>
-<!-- English entry:
-<entry id="view-checkout">
-    <title>Checkout View</title>
-    <para>
-      The Checkout View shows a file contents at a revision point, along with
-      the log entry for that revision.
-    </para>
-  </entry>--><!-- English entry:
-<entry id="view-checkout">
-    <title>Checkout View</title>
-    <para>
-      The Checkout View shows a file contents at a revision point, along with
-      the log entry for that revision.
-    </para>
-  </entry>--></entry>
-  <entry id="view-annotate" md5="b13f3d6bac6e4dddeec18ce03dd099cf" state="changed">
-    <title>Vista anotada</title>
-    <heading>Vista anotada</heading>
-    <para>
-    La vista anotada muestra un archivo con las anotaciones de qu&#xE9; autores son responsables de qu&#xE9; partes del contenido del archivo (y por lo tanto se le conoce a menudo como la vista "culpable")
-    </para>
-<!-- English entry:
-<entry id="view-annotate">
-    <title>Annotate View</title>
-    <para>
-      The Annotated View shows a file annotated with which authors are
-      responsible for which portions of a file's contents (and is hence often
-      called the "blame" view).
-    </para>
-  </entry>--><!-- English entry:
-<entry id="view-annotate">
-    <title>Annotate View</title>
-    <para>
-      The Annotated View shows a file annotated with which authors are
-      responsible for which portions of a file's contents (and is hence often
-      called the "blame" view).
-    </para>
-  </entry>--></entry>
-  <entry id="view-branch" md5="3a5e4bdacbac2ebf33ed29a101accac4" state="changed">
-    <title>Vista ramificada</title>
-    <heading>Vista ramificada (Hist&#xF3;rica)</heading>
-    <para>
-    La vista ramificada muestra la historia de versiones de un archivo, distribuida en ramas. &#xC9;sto permite comprobar, de forma visual, d&#xF3;nde se crearon los puntos de ramificaci&#xF3;n del &#xE1;rbol.
-    </para>
-<!-- English entry:
-<entry id="view-branch">
-    <title>Branch View</title>
-    <para>
-      The Branch View shows a file's version history, layed out by
-      branches. This allows you to visually see where branch points were
-      created in the tree.
-    </para>
-  </entry>--><!-- English entry:
-<entry id="view-branch">
-    <title>Branch View</title>
-    <para>
-      The Branch View shows a file's version history, layed out by
-      branches. This allows you to visually see where branch points were
-      created in the tree.
-    </para>
-  </entry>--></entry>
-  <entry id="view-log" md5="39c6e0be53135cb52e60b8694ba16428" state="changed">
-    <title>Vista de registro</title>
-    <heading>Vista de registro</heading>
-    <para>
-    La vista de registro muestra la historia de un archivo mediante los mensajes de registro de comprobaci&#xF3;n. &#xC9;sto permite ver una historia de los cambios desde el punto de vista de los mensajes de registro que se introdujeron en el momento de la revisi&#xF3;n.
-    </para>
-<!-- English entry:
-<entry id="view-log">
-    <title>Log View</title>
-    <para>
-      The Log View shows a file's history via the checkin log messages. This
-      allows you to see a history of changes from the point of view log
-      messages entered at checkin time.
-    </para>
-  </entry>--><!-- English entry:
-<entry id="view-log">
-    <title>Log View</title>
-    <para>
-      The Log View shows a file's history via the checkin log messages. This
-      allows you to see a history of changes from the point of view log
-      messages entered at checkin time.
-    </para>
-  </entry>--></entry>
-  <entry id="view-diff" md5="662b414167a7427ca87b6063ac9194be" state="changed">
-    <title>Vista de diferencias</title>
-    <heading>Vista de diferencias</heading>
-    <para>
-    La vista de diferencias muestra las diferencias entre dos revisiones distintas de un archivo. &#xC9;sto le permite ver f&#xE1;cilmente los cambios de un archivo. Puede ver las diferencias en varios formatos, incluyendo entre otros un listado coloreado conjunto de las dos revisiones, o un listado de diferencias mediante comandos del editor ed.
-    </para>
-<!-- English entry:
-<entry id="view-diff">
-    <title>Differences View</title>
-    <para>
-      The Differnences View shows the differences between two different
-      revisions of a file. This allows you to easily see how a file has
-      changed.  You can view the differences in various formats, including
-      among others a color coded side by side listing of the two revisions, or
-      in terms of ed editor commands.
-    </para>
-  </entry>--><!-- English entry:
-<entry id="view-diff">
-    <title>Differences View</title>
-    <para>
-      The Differnences View shows the differences between two different
-      revisions of a file. This allows you to easily see how a file has
-      changed.  You can view the differences in various formats, including
-      among others a color coded side by side listing of the two revisions, or
-      in terms of ed editor commands.
-    </para>
-  </entry>--></entry>
-  <entry id="show-deleted-files" md5="6b4878d4170900daebb8ccb308a34327" state="changed">
-    <title>Mostrar/Ocultar archivos eliminados</title>
-    <heading>Mostrar/Ocultar archivos eliminados</heading>
-    <para>
-    Puede optar por ver los archivos eliminados (de Attic) o por no mostrarlos (ocultarlos) en la vista de archivos cvs. Si prefiere mostrarlos, se ver&#xE1;n atenuados y con un icono de <i>papelera</i>.
-    </para>
-    <para>
-    La opci&#xF3;n por omisi&#xF3;n es no mostrarlos.
-    </para>
-<!-- English entry:
-<entry id="show-deleted-files">
-    <title>Show/Hide Deleted Files</title>
-    <para>
-      You can choose to either view deleted files (from the Attic) or to not
-      show (hide) deleted files in the file view.  If you choose to show
-      deleted files, they will show up dimmed and with a <i>trash can</i>
-      icon.
-    </para>
-    <para>
-      The default is not to show deleted files.
-    </para>
-  </entry>--><!-- English entry:
-<entry id="show-deleted-files">
-    <title>Show/Hide Deleted Files</title>
-    <para>
-      You can choose to either view deleted files (from the Attic) or to not
-      show (hide) deleted files in the file view.  If you choose to show
-      deleted files, they will show up dimmed and with a <i>trash can</i>
-      icon.
-    </para>
-    <para>
-      The default is not to show deleted files.
-    </para>
-  </entry>--></entry>
+	<entry id="Overview">
+		<title>Introducci&#xF3;n a Chora</title>
+		<para>Chora es una herramienta para examinar dep&#xF3;sitos de c&#xF3;digo gestionados mediante sistemas de control de c&#xF3;digo. Ofrece vista de directorio (archivos), historial del registro, diferencias entre versiones, vista ramificada gr&#xE1;fica y vista de anotaciones.</para>
+	</entry>
+  
+	<entry id="chora-button">
+		<title>Botones del men&#xFA;: Examinar</title>
+		<para>El bot&#xF3;n Examinar le devuelve a la visualizaci&#xF3;n principal de archivos de Chora del dep&#xF3;sito por omisi&#xF3;n.</para>
+	</entry>
+
+	<entry id="options-button">
+		<title>Botones del men&#xFA;: Opciones</title>
+		<para>El bot&#xF3;n Opciones le permite configurar varias preferencias personales para controlar el comportamiento de Chora.</para>
+	</entry>
+
+	<entry id="help-button">
+		<title>Botones del men&#xFA;: Ayuda</title>
+		<para>El bot&#xF3;n Ayuda le muestra esta pantalla de ayuda.</para>
+	</entry>
+	
+	<entry id="view-checkout">
+		<title>Vista de inspecci&#xF3;n</title>
+		<para>La vista de inspecci&#xF3;n muestra el contenido de un archivo en un punto de revisi&#xF3;n, junto con las entradas de registro de dicha revisi&#xF3;n.</para>
+	</entry>
+
+	<entry id="view-annotate">
+		<title>Vista anotada</title>
+		<para>La vista anotada muestra un archivo con las anotaciones de qu&#xE9; autores son responsables de qu&#xE9; partes del contenido del archivo (y por lo tanto se le conoce a menudo como la vista de la "culpa")</para>
+	</entry>
+
+	<entry id="view-branch">
+		<title>Vista ramificada</title>
+		<para>La vista ramificada muestra la historia de versiones de un archivo, distribuida en ramas. &#xC9;sto permite comprobar, de forma visual, d&#xF3;nde se crearon los puntos de ramificaci&#xF3;n del &#xE1;rbol.</para>
+	</entry>
+
+	<entry id="view-log">
+		<title>Vista de registro</title>
+		<para>La vista de registro muestra la historia de un archivo mediante los mensajes de registro de comprobaci&#xF3;n. &#xC9;sto permite ver una historia de los cambios desde el punto de vista de los mensajes de registro que se introdujeron en el momento de la revisi&#xF3;n.</para>
+	</entry>
+
+	<entry id="view-diff">
+		<title>Vista de diferencias</title>
+		<para>La vista de diferencias muestra las diferencias entre dos revisiones distintas de un archivo. &#xC9;sto le permite ver f&#xE1;cilmente los cambios de un archivo. Puede ver las diferencias en varios formatos, incluyendo entre otros un listado coloreado conjunto de las dos revisiones, o un listado de diferencias mediante comandos del editor ed.</para>
+	</entry>
+
+	<entry id="show-deleted-files">
+		<title>Mostrar/Ocultar archivos eliminados</title>
+		<para>Puede optar por ver los archivos eliminados (desde Attic) o por no mostrarlos (ocultarlos) en la vista de archivos. Si prefiere mostrarlos, se ver&#xE1;n atenuados y con un icono de <i>papelera</i>.</para>
+		<para>La opci&#xF3;n por omisi&#xF3;n es no mostrarlos.</para>
+	</entry>
 </help>
diff --git a/locale/fi_FI/LC_MESSAGES/chora.mo b/locale/fi_FI/LC_MESSAGES/chora.mo
index 52280f0..15f3890 100644
Binary files a/locale/fi_FI/LC_MESSAGES/chora.mo and b/locale/fi_FI/LC_MESSAGES/chora.mo differ
diff --git a/locale/fi_FI/help.xml b/locale/fi_FI/help.xml
index 37fc42e..5033d2b 100644
--- a/locale/fi_FI/help.xml
+++ b/locale/fi_FI/help.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!-- $Horde: chora/locale/fi_FI/help.xml,v 1.1.2.2 2006/02/20 11:20:35 jan Exp $ -->
+<!-- $Horde: chora/locale/fi_FI/help.xml,v 1.1.2.4 2008/10/09 17:40:14 jan Exp $ -->
 <help>
   <entry id="Overview" md5="6c904e7e17dea0fc75802e2c1a534b03" state="uptodate">
     <title>Chora yleiskuva</title>
diff --git a/locale/fr_FR/LC_MESSAGES/chora.mo b/locale/fr_FR/LC_MESSAGES/chora.mo
index 54ed683..b068c45 100644
Binary files a/locale/fr_FR/LC_MESSAGES/chora.mo and b/locale/fr_FR/LC_MESSAGES/chora.mo differ
diff --git a/locale/it_IT/LC_MESSAGES/chora.mo b/locale/it_IT/LC_MESSAGES/chora.mo
new file mode 100644
index 0000000..5337cc8
Binary files /dev/null and b/locale/it_IT/LC_MESSAGES/chora.mo differ
diff --git a/locale/nb_NO/LC_MESSAGES/chora.mo b/locale/nb_NO/LC_MESSAGES/chora.mo
index 7ebb8ef..7445a4d 100644
Binary files a/locale/nb_NO/LC_MESSAGES/chora.mo and b/locale/nb_NO/LC_MESSAGES/chora.mo differ
diff --git a/locale/nl_NL/LC_MESSAGES/chora.mo b/locale/nl_NL/LC_MESSAGES/chora.mo
index 7a003b1..d7d41bf 100644
Binary files a/locale/nl_NL/LC_MESSAGES/chora.mo and b/locale/nl_NL/LC_MESSAGES/chora.mo differ
diff --git a/locale/pl_PL/LC_MESSAGES/chora.mo b/locale/pl_PL/LC_MESSAGES/chora.mo
index af3e92f..f72cde8 100644
Binary files a/locale/pl_PL/LC_MESSAGES/chora.mo and b/locale/pl_PL/LC_MESSAGES/chora.mo differ
diff --git a/locale/pt_BR/LC_MESSAGES/chora.mo b/locale/pt_BR/LC_MESSAGES/chora.mo
index eb8154c..c722fe7 100644
Binary files a/locale/pt_BR/LC_MESSAGES/chora.mo and b/locale/pt_BR/LC_MESSAGES/chora.mo differ
diff --git a/locale/ro_RO/LC_MESSAGES/chora.mo b/locale/ro_RO/LC_MESSAGES/chora.mo
index aa115ec..7fa7476 100644
Binary files a/locale/ro_RO/LC_MESSAGES/chora.mo and b/locale/ro_RO/LC_MESSAGES/chora.mo differ
diff --git a/locale/ru_RU/LC_MESSAGES/chora.mo b/locale/ru_RU/LC_MESSAGES/chora.mo
index be04998..b18da60 100644
Binary files a/locale/ru_RU/LC_MESSAGES/chora.mo and b/locale/ru_RU/LC_MESSAGES/chora.mo differ
diff --git a/locale/sk_SK/LC_MESSAGES/chora.mo b/locale/sk_SK/LC_MESSAGES/chora.mo
index 77f8798..9815d9a 100644
Binary files a/locale/sk_SK/LC_MESSAGES/chora.mo and b/locale/sk_SK/LC_MESSAGES/chora.mo differ
diff --git a/locale/sv_SE/LC_MESSAGES/chora.mo b/locale/sv_SE/LC_MESSAGES/chora.mo
index de1fba8..9b8cad4 100644
Binary files a/locale/sv_SE/LC_MESSAGES/chora.mo and b/locale/sv_SE/LC_MESSAGES/chora.mo differ
diff --git a/locale/zh_CN/LC_MESSAGES/chora.mo b/locale/zh_CN/LC_MESSAGES/chora.mo
index 421b52d..05c6960 100644
Binary files a/locale/zh_CN/LC_MESSAGES/chora.mo and b/locale/zh_CN/LC_MESSAGES/chora.mo differ
diff --git a/locale/zh_TW/LC_MESSAGES/chora.mo b/locale/zh_TW/LC_MESSAGES/chora.mo
index 564acfb..20189d4 100644
Binary files a/locale/zh_TW/LC_MESSAGES/chora.mo and b/locale/zh_TW/LC_MESSAGES/chora.mo differ
diff --git a/patchsets.php b/patchsets.php
index c44d388..bbb7a8f 100644
--- a/patchsets.php
+++ b/patchsets.php
@@ -1,12 +1,15 @@
 <?php
 /**
- * $Horde: chora/patchsets.php,v 1.18.8.6 2007/02/23 03:58:53 selsky Exp $
+ * $Horde: chora/patchsets.php,v 1.18.8.8 2009/01/06 15:22:34 jan Exp $
  *
- * Copyright 1999-2007 Anil Madhavapeddy <anil at recoil.org>
- * Copyright 1999-2007 Charles Hagenbuch <chuck at horde.org>
+ * Copyright 1999-2009 The Horde Project (http://www.horde.org/)
  *
- * See the enclosed file COPYING for license information (GPL).  If you
+ * 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  Anil Madhavapeddy <anil at recoil.org>
+ * @author  Chuck Hagenbuch <chuck at horde.org>
+ * @package Chora
  */
 
 @define('CHORA_BASE', dirname(__FILE__));
@@ -19,28 +22,31 @@ if (empty($conf['paths']['cvsps']) && !is_a($VC, 'VC_svn')) {
 }
 
 if (@is_dir($fullname)) {
-    Chora::fatal('No patchsets for directories yet.');
+    Chora::fatal('403 Forbidden', 'No patchsets for directories yet.');
 }
 
 if (!$VC->isFile($fullname)) {
-    Chora::fatal("$where: no such file or directory");
+    Chora::fatal('404 Not Found', "$where: no such file or directory");
 }
 
 $ps = $VC->getPatchsetObject($where, $cache);
 Chora::checkError($ps);
 
-$title = sprintf(_("Patchsets for %s"), Text::htmlallspaces($where));
-$upwhere = preg_replace('|[^/]+$|', '', $where);
+$title = sprintf(_("Patchsets for %s"), $where);
 $extraLink = Chora::getFileViews();
 
+Horde::addScriptFile('prototype.js', 'chora', true);
+Horde::addScriptFile('tables.js', 'chora', true);
+Horde::addScriptFile('QuickFinder.js', 'chora', true);
 require CHORA_TEMPLATES . '/common-header.inc';
 require CHORA_TEMPLATES . '/menu.inc';
 require CHORA_TEMPLATES . '/headerbar.inc';
+require CHORA_TEMPLATES . '/patchsets/header.inc';
 
 $patchsets = $ps->_patchsets;
 krsort($patchsets);
 foreach ($patchsets as $id => $patchset) {
-    $commitDate = @strftime('%c', $patchset['date']);
+    $commitDate = Chora::formatTime($patchset['date']);
     $readableDate = Chora::readableTime($patchset['date'], true);
     $author = Chora::showAuthorName($patchset['author'], true);
     if (is_a($VC, 'VC_svn')) {
@@ -49,11 +55,11 @@ foreach ($patchsets as $id => $patchset) {
         $topDir = substr($where, 0, strpos($where, '/', 1));
 
         // Subversion supports patchset diffs natively.
-        $allDiffsLink = Horde::link(Chora::url('diff', $topDir, array('r1' => $id - 1, 'r2' => $id, 'ty' => 'u')),
-                                    _("Diff All Files")) . _("Diff All Files") . '</a>';
+        $patchset_link = Horde::link(Chora::url('diff', $topDir, array('r1' => $id - 1, 'r2' => $id, 't' => 'unified'))) .
+            $id . '</a>';
     } else {
         // Not supported in any other VC systems yet.
-        $allDiffsLink = '';
+        $patchset_link = $id;
     }
 
     $files = array();
@@ -61,19 +67,19 @@ foreach ($patchsets as $id => $patchset) {
     foreach ($patchset['members'] as $member) {
         $file = array();
         $mywhere = is_a($VC, 'VC_svn') ? $member['file'] : $dir . '/' . $member['file'];
-        $file['file'] = Horde::link(Chora::url('patchsets', $mywhere), $member['file']) . $member['file'] . '</a>';
+        $file['file'] = Horde::link(Chora::url('patchsets', $mywhere)) . htmlspecialchars($member['file']) . '</a>';
         if ($member['from'] == 'INITIAL') {
-            $file['from'] = '<i>' . _("New File") . '</i>';
+            $file['from'] = '<ins>' . _("New File") . '</ins>';
             $file['diff'] = '';
         } else {
-            $file['from'] = Horde::link(Chora::url('co', $mywhere, array('r' => $member['from'])), $member['from']) . $member['from'] . '</a>';
-            $file['diff'] = Horde::link(Chora::url('diff', $mywhere, array('r1' => $member['from'], 'r2' => $member['to'], 'ty' => 'u')), _("Diff")) . '(' . _("Diff") . ')';
+            $file['from'] = Horde::link(Chora::url('co', $mywhere, array('r' => $member['from']))) . htmlspecialchars($member['from']) . '</a>';
+            $file['diff'] = Horde::link(Chora::url('diff', $mywhere, array('r1' => $member['from'], 'r2' => $member['to'], 't' => 'unified'))) . ' ' . Horde::img('diff.png', _("Diff")) . '</a>';
         }
         if (substr($member['to'], -6) == '(DEAD)') {
-            $file['to'] = '<i>' . _("Deleted") . '</i>';
+            $file['to'] = '<del>' . _("Deleted") . '</del>';
             $file['diff'] = '';
         } else {
-            $file['to'] = Horde::link(Chora::url('co', $mywhere, array('r' => $member['to'])), $member['to']) . $member['to'] . '</a>';
+            $file['to'] = Horde::link(Chora::url('co', $mywhere, array('r' => $member['to']))) . htmlspecialchars($member['to']) . '</a>';
         }
 
         $files[] = $file;
@@ -83,4 +89,5 @@ foreach ($patchsets as $id => $patchset) {
     require CHORA_TEMPLATES . '/patchsets/ps.inc';
 }
 
+require CHORA_TEMPLATES . '/patchsets/footer.inc';
 require $registry->get('templates', 'horde') . '/common-footer.inc';
diff --git a/po/chora.pot b/po/chora.pot
index 804e54f..801b882 100644
--- a/po/chora.pot
+++ b/po/chora.pot
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: dev at lists.horde.org\n"
-"POT-Creation-Date: 2006-03-06 12:47+0100\n"
+"POT-Creation-Date: 2009-03-19 10:37+0100\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
@@ -20,43 +20,54 @@ msgstr ""
 msgid "#"
 msgstr ""
 
-#: templates/patchsets/ps.inc:12 templates/log/rev.inc:26
+#: templates/log/header.inc:17
 #, php-format
-msgid "%s ago"
+msgid ""
+"%1$s shows diffs to the previous revision. If you select a revision by "
+"clicking its row, %1$s will show the differences to the selected row."
+msgstr ""
+
+#: co.php:84
+#, php-format
+msgid "%s Revision %s (%s ago)"
 msgstr ""
 
-#: templates/diff/hr/footer.inc:19
+#: templates/annotate/footer.inc:22
 #, php-format
-msgid "Added in v.%s"
+msgid "%s ago"
 msgstr ""
 
-#: templates/log/request.inc:57
+#: templates/diff/hr/header.inc:25
+msgid "Added"
+msgstr ""
+
+#: templates/log/header.inc:31
 msgid "All Branches"
 msgstr ""
 
-#: co.php:72 templates/log/rev.inc:8
+#: co.php:89
 msgid "Annotate"
 msgstr ""
 
-#: templates/directory/header.inc:18 templates/stats/stats.inc:3
-#: templates/annotate/header.inc:5
+#: templates/directory/header.inc:12 templates/patchsets/header.inc:15
+#: templates/stats/stats.inc:4 templates/annotate/header.inc:5
+#: templates/log/header.inc:49
 msgid "Author"
 msgstr ""
 
-#: templates/directory/back.inc:3
-msgid "Back"
+#: templates/checkout/checkout.inc:4 templates/diff/hr/header.inc:35
+msgid "Author:"
 msgstr ""
 
-#: templates/checkout/checkout.inc:27 templates/log/rev.inc:31
-msgid "Branch Point for:"
+#: templates/directory/back.inc:4
+msgid "Back"
 msgstr ""
 
-#: templates/checkout/checkout.inc:11 templates/patchsets/ps.inc:14
-#: templates/log/rev.inc:28
+#: templates/checkout/checkout.inc:5 templates/diff/hr/header.inc:37
 msgid "Branch:"
 msgstr ""
 
-#: lib/Chora.php:465
+#: lib/Chora.php:502 lib/Chora.php:503 lib/Chora.php:507 lib/Chora.php:508
 msgid "Branches"
 msgstr ""
 
@@ -64,49 +75,38 @@ msgstr ""
 msgid "Branching to"
 msgstr ""
 
-#: templates/checkout/checkout.inc:35
-#, php-format
-msgid "Changed since <b>%s</b>"
-msgstr ""
-
-#: templates/log/rev.inc:37
-#, php-format
-msgid "Changed since <strong>%s</strong>"
+#: lib/Chora.php:392
+msgid "Change repositories:"
 msgstr ""
 
-#: templates/history/rev.inc:8
+#: templates/history/rev.inc:7
 #, php-format
 msgid "Changed: %s"
 msgstr ""
 
-#: co.php:70
-#, php-format
-msgid "Checkout of %s (revision %s)"
+#: templates/checkout/checkout.inc:12
+msgid "Checkout"
 msgstr ""
 
-#: templates/cvsgraph/cvsgraph.inc:11
+#: templates/cvsgraph/cvsgraph.inc:5
 msgid ""
 "Click on the links in between revisions to get a diff between those "
 "revisions."
 msgstr ""
 
-#: templates/cvsgraph/cvsgraph.inc:10
+#: templates/cvsgraph/cvsgraph.inc:4
 msgid "Click on the revisions and branches to display the file."
 msgstr ""
 
-#: templates/log/rev.inc:42 templates/log/rev.inc:50
-msgid "Colored"
-msgstr ""
-
-#: templates/checkout/checkout.inc:19 templates/patchsets/ps.inc:17
-msgid "Commit Tags:"
+#: templates/diff/hr/header.inc:13
+msgid "Column"
 msgstr ""
 
-#: templates/stats/stats.inc:4
+#: templates/stats/stats.inc:5
 msgid "Commits"
 msgstr ""
 
-#: templates/log/request.inc:28
+#: templates/diff/hr/header.inc:12
 msgid "Context"
 msgstr ""
 
@@ -114,58 +114,54 @@ msgstr ""
 msgid "Customize tasks to run upon logging in to Chora."
 msgstr ""
 
-#: templates/directory/header.inc:23
+#: templates/directory/header.inc:15
 msgid "Da_te"
 msgstr ""
 
-#: templates/directory/header.inc:23
+#: templates/directory/header.inc:15 templates/patchsets/header.inc:14
+#: templates/log/header.inc:48
 msgid "Date"
 msgstr ""
 
-#: patchsets.php:73
-msgid "Deleted"
+#: templates/diff/hr/header.inc:36
+msgid "Date:"
 msgstr ""
 
-#: templates/directory/file.inc:5 templates/directory/file.inc:17
-msgid "Deleted File"
+#: patchsets.php:79
+msgid "Deleted"
 msgstr ""
 
-#: templates/log/rev.inc:13
-msgid "Deselect"
+#: templates/directory/file.inc:5 templates/directory/file.inc:28
+msgid "Deleted File"
 msgstr ""
 
-#: patchsets.php:70
+#: patchsets.php:76 templates/log/header.inc:16
 msgid "Diff"
 msgstr ""
 
-#: patchsets.php:53
-msgid "Diff All Files"
-msgstr ""
-
-#: diff.php:69
+#: diff.php:71
 #, php-format
 msgid "Diff for %s between version %s and %s"
 msgstr ""
 
-#: templates/log/rev.inc:49
-#, php-format
-msgid "Diffs to <strong>%s</strong>"
+#: templates/directory/dir.inc:4 templates/directory/dir.inc:6
+msgid "Directory"
 msgstr ""
 
-#: templates/log/rev.inc:41
-#, php-format
-msgid "Diffs to version %s"
+#: annotate.php:33 co.php:90
+msgid "Download"
 msgstr ""
 
-#: templates/directory/dir.inc:4 templates/directory/dir.inc:6
-msgid "Directory"
+#: templates/diff/hr/header.inc:9
+msgid "Download diff as: "
 msgstr ""
 
-#: annotate.php:30 co.php:73 templates/log/rev.inc:10
-msgid "Download"
+#: templates/checkout/checkout.inc:23
+#, php-format
+msgid "Download revision %s"
 msgstr ""
 
-#: templates/log/request.inc:30
+#: templates/diff/hr/header.inc:14
 msgid "Ed Script"
 msgstr ""
 
@@ -177,120 +173,106 @@ msgstr ""
 msgid "Error Encountered"
 msgstr ""
 
-#: templates/directory/file.inc:7 templates/directory/file.inc:19
-#: templates/directory/header.inc:8
+#: templates/directory/file.inc:7 templates/directory/file.inc:30
+#: templates/directory/header.inc:6
 msgid "File"
 msgstr ""
 
-#: templates/patchsets/ps.inc:21
-msgid "Files Changed:"
-msgstr ""
-
-#: templates/log/request.inc:44
-msgid "Get Diffs"
+#: templates/patchsets/header.inc:16
+msgid "Files"
 msgstr ""
 
-#: lib/Chora.php:468
-msgid "Graph"
+#: templates/diff/hr/header.inc:16
+msgid "Get Diff"
 msgstr ""
 
-#: cvsgraph.php:48
+#: cvsgraph.php:51
 #, php-format
 msgid "Graph for %s"
 msgstr ""
 
-#: browse.php:40
+#: browse.php:42
 msgid "Hide Deleted Files"
 msgstr ""
 
-#: browse.php:40
+#: browse.php:44
 msgid "Hide _Deleted Files"
 msgstr ""
 
-#: templates/log/request.inc:26
-msgid "Human Readable"
-msgstr ""
-
-#: templates/directory/header.inc:25
+#: templates/directory/header.inc:17
 msgid "Last Log"
 msgstr ""
 
-#: templates/diff/hr/header.inc:5
-#, php-format
-msgid "Last Log Message for rev %s:"
-msgstr ""
-
-#: templates/diff/hr/footer.inc:5
-msgid "Legend:"
-msgstr ""
-
-#: templates/annotate/header.inc:7
+#: templates/annotate/header.inc:8
 msgid "Line"
 msgstr ""
 
-#: templates/diff/hr/row.inc:3 templates/diff/hr/row.inc:7
+#: templates/diff/hr/row.inc:3 templates/diff/hr/row.inc:6
 #, php-format
 msgid "Line %s"
 msgstr ""
 
-#: templates/headerbar.inc:11
+#: templates/headerbar.inc:5
 msgid "Location:"
 msgstr ""
 
-#: templates/checkout/checkout.inc:42
-msgid "Log:"
+#: templates/checkout/checkout.inc:1 templates/patchsets/header.inc:17
+#: templates/diff/hr/header.inc:30 templates/log/header.inc:50
+msgid "Log Message"
 msgstr ""
 
 #: config/prefs.php.dist:10
 msgid "Login Tasks"
 msgstr ""
 
-#: lib/Chora.php:459
+#: lib/Chora.php:488 lib/Chora.php:489
 msgid "Logs"
 msgstr ""
 
-#: templates/log/rev.inc:43 templates/log/rev.inc:51
-msgid "Long"
+#: diff.php:44
+msgid "Malformed Query"
 msgstr ""
 
-#: diff.php:61
-msgid "Malformed Query"
+#: lib/Block/tree_menu.php:3
+msgid "Menu List"
+msgstr ""
+
+#: templates/diff/hr/header.inc:26
+msgid "Modified"
 msgstr ""
 
-#: patchsets.php:66
+#: patchsets.php:72
 msgid "New File"
 msgstr ""
 
-#: templates/diff/hr/nochange.inc:6
-msgid "No Viewable Change"
+#: templates/diff/hr/nochange.inc:3
+msgid "No Visible Changes"
 msgstr ""
 
-#: templates/log/rev.inc:44 templates/log/rev.inc:52
-msgid "NoWhitespaceChanges"
+#: templates/log/footer.inc:8
+#, php-format
+msgid ""
+"Only showing the 100 latest revisions. %sShow all revisions?</a> (may take a "
+"while)."
 msgstr ""
 
 #: config/prefs.php.dist:9
 msgid "Other Options"
 msgstr ""
 
-#: lib/Chora.php:368
-msgid "Other Repositories"
-msgstr ""
-
-#: templates/directory/back.inc:3
+#: templates/directory/back.inc:4
 msgid "Parent Directory"
 msgstr ""
 
-#: templates/patchsets/ps.inc:6
-#, php-format
-msgid "PatchSet %s</span> by %s"
+#: templates/patchsets/header.inc:13
+msgid "Patchset"
 msgstr ""
 
-#: lib/Chora.php:462
+#: lib/Chora.php:495 lib/Chora.php:496
 msgid "Patchsets"
 msgstr ""
 
-#: patchsets.php:32
+#: patchsets.php:35
 #, php-format
 msgid "Patchsets for %s"
 msgstr ""
@@ -299,78 +281,91 @@ msgstr ""
 msgid "Please contact"
 msgstr ""
 
-#: templates/diff/hr/footer.inc:11
-#, php-format
-msgid "Removed in v.%s"
+#: templates/annotate/header.inc:7
+msgid "Prev"
+msgstr ""
+
+#: templates/diff/hr/header.inc:27
+msgid "Removed"
 msgstr ""
 
-#: templates/log/request.inc:14
-msgid "Retrieve diffs between:"
+#: lib/api.php:30
+msgid "Repositories"
 msgstr ""
 
-#: templates/directory/header.inc:13 templates/annotate/header.inc:6
+#: templates/directory/header.inc:9 templates/annotate/header.inc:6
 msgid "Rev"
 msgstr ""
 
-#: co.php:77
-#, php-format
-msgid "Revision %s for file %s not found."
+#: templates/log/header.inc:47
+msgid "Revision"
 msgstr ""
 
-#: templates/checkout/checkout.inc:4
+#: browse.php:157
 #, php-format
-msgid "Revision <b>%s</b>, <i>%s</i> (%s ago) by %s"
+msgid "Revisions for %s"
+msgstr ""
+
+#: templates/patchsets/header.inc:4
+msgid "Search Patchsets:"
+msgstr ""
+
+#: templates/log/header.inc:4
+msgid "Search Revisions:"
 msgstr ""
 
-#: templates/log/rev.inc:15
-msgid "Select for Diff"
+#: templates/log/header.inc:29
+msgid "Show Branch:"
 msgstr ""
 
 #: browse.php:42
 msgid "Show Deleted Files"
 msgstr ""
 
-#: browse.php:42
+#: templates/log/header.inc:18
+msgid "Show Differences"
+msgstr ""
+
+#: browse.php:44
 msgid "Show _Deleted Files"
 msgstr ""
 
-#: templates/log/request.inc:29
-msgid "Side-by-Side"
+#: templates/log/rev.inc:4
+msgid "Show changes to the previous revision"
 msgstr ""
 
-#: templates/directory/header.inc:7 templates/directory/header.inc:12
-#: templates/directory/header.inc:17 templates/directory/header.inc:22
-msgid "Sort Order"
+#: templates/log/rev.inc:6
+msgid "Show changes to the selected revision"
 msgstr ""
 
-#: annotate.php:27
+#: templates/log/header.inc:13
 #, php-format
-msgid "Source Annotation of %s for version %s"
+msgid "Show diffs between %s and %s"
 msgstr ""
 
-#: history.php:160
+#: annotate.php:30
 #, php-format
-msgid "Source Branching View for %s"
+msgid "Source Annotation of %s (revision %s)"
 msgstr ""
 
-#: browse.php:32
+#: history.php:163
 #, php-format
-msgid "Source Directory of /%s"
+msgid "Source Branching View for %s"
 msgstr ""
 
-#: browse.php:126
+#: browse.php:35
 #, php-format
-msgid "Source Log for %s"
+msgid "Source Directory of /%s"
 msgstr ""
 
-#: lib/Chora.php:182
+#: lib/Chora.php:179
 msgid ""
 "SourceRoot not found! This could be a misconfiguration by the server "
 "administrator, or the server could be having temporary problems. Please try "
 "again later."
 msgstr ""
 
-#: lib/Chora.php:470
+#: lib/Chora.php:513 lib/Chora.php:514
 msgid "Statistics"
 msgstr ""
 
@@ -379,102 +374,80 @@ msgstr ""
 msgid "Statistics for %s"
 msgstr ""
 
-#: templates/log/rev.inc:34
-msgid "Tags:"
+#: templates/patchsets/ps.inc:22 templates/log/rev.inc:14
+msgid "Tags"
 msgstr ""
 
-#: templates/log/request.inc:5
-msgid ""
-"This form allows you to request diffs between any two revisions of a file.  "
-"You may select a symbolic revision name using the selection box or you may "
-"type in a numeric name using the type-in text box."
+#: templates/checkout/checkout.inc:6 templates/diff/hr/header.inc:38
+msgid "Tags:"
 msgstr ""
 
-#: templates/headerbar.inc:16
+#: templates/headerbar.inc:9
 msgid "Tracking Branch"
 msgstr ""
 
-#: templates/log/request.inc:23
-msgid "Type:"
-msgstr ""
-
-#: templates/log/request.inc:27
+#: templates/diff/hr/header.inc:11
 msgid "Unified"
 msgstr ""
 
-#: templates/log/request.inc:18 templates/log/request.inc:38
-msgid "Use Text Field"
+#: templates/diff/hr/header.inc:24
+msgid "Unmodified"
 msgstr ""
 
 #: config/prefs.php.dist:37
 msgid "Use last viewed file or directory at login time"
 msgstr ""
 
-#: annotate.php:29 templates/log/rev.inc:9
-msgid "View"
-msgstr ""
-
-#: templates/log/request.inc:60
-msgid "View Branch"
+#: templates/diff/hr/header.inc:48 templates/diff/hr/header.inc:49
+#, php-format
+msgid "Version %s"
 msgstr ""
 
-#: templates/log/request.inc:54
-msgid "View revisions on:"
+#: annotate.php:32 templates/log/header.inc:34
+msgid "View"
 msgstr ""
 
-#: lib/Chora.php:472
+#: lib/Chora.php:517
 msgid "View:"
 msgstr ""
 
-#: templates/directory/header.inc:18
+#: templates/directory/header.inc:12
 msgid "_Author"
 msgstr ""
 
-#: lib/Chora.php:465
+#: lib/Chora.php:504 lib/Chora.php:509
 msgid "_Branches"
 msgstr ""
 
-#: lib/Chora.php:439
+#: lib/Chora.php:469
 msgid "_Browse"
 msgstr ""
 
-#: templates/directory/header.inc:8
+#: templates/directory/header.inc:6
 msgid "_File"
 msgstr ""
 
-#: lib/Chora.php:468
-msgid "_Graph"
-msgstr ""
-
-#: lib/Chora.php:459
+#: lib/Chora.php:490
 msgid "_Logs"
 msgstr ""
 
-#: lib/Chora.php:462
+#: lib/Chora.php:497
 msgid "_Patchsets"
 msgstr ""
 
-#: templates/directory/header.inc:13
+#: templates/directory/header.inc:9
 msgid "_Rev"
 msgstr ""
 
-#: lib/Chora.php:470
+#: lib/Chora.php:515
 msgid "_Statistics"
 msgstr ""
 
-#: templates/log/request.inc:35
-msgid "and:"
-msgstr ""
-
-#: templates/history/rev.inc:4 templates/log/rev.inc:5
+#: templates/history/rev.inc:3
 #, php-format
 msgid "by %s"
 msgstr ""
 
-#: templates/diff/hr/footer.inc:15
-msgid "changed lines"
-msgstr ""
-
 #: lib/Chora.php:31
 msgid "day"
 msgstr ""
@@ -524,11 +497,6 @@ msgstr ""
 msgid "seconds"
 msgstr ""
 
-#: templates/diff/hr/header.inc:13 templates/diff/hr/header.inc:14
-#, php-format
-msgid "version %s"
-msgstr ""
-
 #: lib/Chora.php:42
 msgid "very little time"
 msgstr ""
diff --git a/po/cs_CZ.po b/po/cs_CZ.po
index da3538a..8f8142e 100644
--- a/po/cs_CZ.po
+++ b/po/cs_CZ.po
@@ -1,5 +1,5 @@
 # Chora Czech Translation.
-# Copyright (C) 2004 Horde Project
+# Copyright 2004-2009 The Horde Project
 # This file is distributed under the same license as the Horde package.
 # Pavel Chytil <pavel at chytil.tk>, 2004.
 #
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: Chora 2.0-cvs\n"
 "Report-Msgid-Bugs-To: dev at lists.horde.org\n"
-"POT-Creation-Date: 2005-10-13 17:16+0200\n"
+"POT-Creation-Date: 2004-11-25 09:19-0800\n"
 "PO-Revision-Date:  2004-11-21 14:56-0800\n"
 "Last-Translator: Pavel Chytil <pavel at chytil.tk>\n"
 "Language-Team: Czech <dev at lists.horde.org>\n"
@@ -21,7 +21,7 @@ msgstr ""
 msgid "#"
 msgstr "èíslo"
 
-#: templates/patchsets/ps.inc:12 templates/log/rev.inc:26
+#: templates/patchsets/ps.inc:12 templates/log/rev.inc:24
 #, php-format
 msgid "%s ago"
 msgstr "pøed %s"
@@ -35,11 +35,11 @@ msgstr "P
 msgid "All Branches"
 msgstr "V¹echny vìtve"
 
-#: co.php:72 templates/log/rev.inc:8
+#: co.php:68 templates/log/rev.inc:8
 msgid "Annotate"
 msgstr "Komentovat"
 
-#: templates/directory/header.inc:18 templates/stats/stats.inc:3
+#: templates/stats/stats.inc:3 templates/directory/header.inc:18
 #: templates/annotate/header.inc:5
 msgid "Author"
 msgstr "Autor"
@@ -48,16 +48,16 @@ msgstr "Autor"
 msgid "Back"
 msgstr "Zpìt"
 
-#: templates/checkout/checkout.inc:27 templates/log/rev.inc:31
+#: templates/log/rev.inc:29 templates/checkout/checkout.inc:27
 msgid "Branch Point for:"
 msgstr "Vìtvící bod pro:"
 
-#: templates/checkout/checkout.inc:11 templates/patchsets/ps.inc:14
-#: templates/log/rev.inc:28
+#: templates/patchsets/ps.inc:14 templates/log/rev.inc:26
+#: templates/checkout/checkout.inc:11
 msgid "Branch:"
 msgstr "Vìtev"
 
-#: lib/Chora.php:465
+#: lib/Chora.php:467
 msgid "Branches"
 msgstr "Vìtve"
 
@@ -65,22 +65,17 @@ msgstr "V
 msgid "Branching to"
 msgstr "Vìtvící se do"
 
-#: templates/checkout/checkout.inc:35
+#: templates/log/rev.inc:35 templates/checkout/checkout.inc:35
 #, php-format
 msgid "Changed since <b>%s</b>"
 msgstr "Zmìny od <b>%s</b>"
 
-#: templates/log/rev.inc:37
-#, fuzzy, php-format
-msgid "Changed since <strong>%s</strong>"
-msgstr "Zmìny od <b>%s</b>"
-
 #: templates/history/rev.inc:8
 #, php-format
 msgid "Changed: %s"
 msgstr "Zmìnìn: %s"
 
-#: co.php:70
+#: co.php:66
 #, php-format
 msgid "Checkout of %s (revision %s)"
 msgstr "Obdr¾ení %s (revize %s)"
@@ -95,11 +90,11 @@ msgstr "Pro obdr
 msgid "Click on the revisions and branches to display the file."
 msgstr "Pro zobrazení souboru, kliknìte na revize a vìtve."
 
-#: templates/log/rev.inc:42 templates/log/rev.inc:50
+#: templates/log/rev.inc:40 templates/log/rev.inc:49
 msgid "Colored"
 msgstr "Zabarvení"
 
-#: templates/checkout/checkout.inc:19 templates/patchsets/ps.inc:17
+#: templates/patchsets/ps.inc:17 templates/checkout/checkout.inc:19
 msgid "Commit Tags:"
 msgstr "Ukládající Znaèky:"
 
@@ -127,7 +122,7 @@ msgstr "Datum"
 msgid "Deleted"
 msgstr "Smazáno"
 
-#: templates/directory/file.inc:5 templates/directory/file.inc:17
+#: templates/directory/file.inc:6 templates/directory/file.inc:18
 msgid "Deleted File"
 msgstr "Smazané Soubory"
 
@@ -148,12 +143,12 @@ msgstr "Diff V
 msgid "Diff for %s between version %s and %s"
 msgstr "Diff pro %s mezi verzemi %s a %s"
 
-#: templates/log/rev.inc:49
-#, fuzzy, php-format
-msgid "Diffs to <strong>%s</strong>"
+#: templates/log/rev.inc:48
+#, php-format
+msgid "Diffs to <b>%s</b>"
 msgstr "Diff k <b>%s</b>"
 
-#: templates/log/rev.inc:41
+#: templates/log/rev.inc:39
 #, php-format
 msgid "Diffs to version %s"
 msgstr "Diffs do verze %s"
@@ -162,7 +157,7 @@ msgstr "Diffs do verze %s"
 msgid "Directory"
 msgstr "Adresáø"
 
-#: annotate.php:30 co.php:73 templates/log/rev.inc:10
+#: annotate.php:30 co.php:69 templates/log/rev.inc:10
 msgid "Download"
 msgstr "Download"
 
@@ -178,7 +173,7 @@ msgstr "Chyba"
 msgid "Error Encountered"
 msgstr "Vyskytla se Chyba"
 
-#: templates/directory/file.inc:7 templates/directory/file.inc:19
+#: templates/directory/file.inc:8 templates/directory/file.inc:20
 #: templates/directory/header.inc:8
 msgid "File"
 msgstr "Soubor"
@@ -191,7 +186,7 @@ msgstr "Zm
 msgid "Get Diffs"
 msgstr "Dostat Diffs"
 
-#: lib/Chora.php:468
+#: lib/Chora.php:470
 msgid "Graph"
 msgstr "Graf"
 
@@ -246,11 +241,11 @@ msgstr "Z
 msgid "Login Tasks"
 msgstr "Úkony Provádìné pøi Pøihlá¹ení"
 
-#: lib/Chora.php:459
+#: lib/Chora.php:461
 msgid "Logs"
 msgstr "Záznamy"
 
-#: templates/log/rev.inc:43 templates/log/rev.inc:51
+#: templates/log/rev.inc:41 templates/log/rev.inc:50
 msgid "Long"
 msgstr "Dlouhý"
 
@@ -266,7 +261,7 @@ msgstr "Nov
 msgid "No Viewable Change"
 msgstr "®ádné Viditelné Zmìny"
 
-#: templates/log/rev.inc:44 templates/log/rev.inc:52
+#: templates/log/rev.inc:42 templates/log/rev.inc:51
 msgid "NoWhitespaceChanges"
 msgstr "®ádné Zmìny Mezer èi Prázdných Øádkù"
 
@@ -274,7 +269,7 @@ msgstr "
 msgid "Other Options"
 msgstr "Dal¹í Mo¾nosti"
 
-#: lib/Chora.php:368
+#: lib/Chora.php:369
 msgid "Other Repositories"
 msgstr "Dal¹í zdroje CVS"
 
@@ -287,7 +282,7 @@ msgstr "Adres
 msgid "PatchSet %s</span> by %s"
 msgstr "PatchSet %s</span> provedl(a) %s"
 
-#: lib/Chora.php:462
+#: lib/Chora.php:464
 msgid "Patchsets"
 msgstr "Patchsets"
 
@@ -313,7 +308,7 @@ msgstr "Obdr
 msgid "Rev"
 msgstr "Rev"
 
-#: co.php:77
+#: co.php:73
 #, php-format
 msgid "Revision %s for file %s not found."
 msgstr "Nebyla nalezena revize %s pro soubor %s."
@@ -364,7 +359,7 @@ msgstr "Zdrojov
 msgid "Source Log for %s"
 msgstr "Zdrojový Záznam pro %s"
 
-#: lib/Chora.php:182
+#: lib/Chora.php:183
 msgid ""
 "SourceRoot not found! This could be a misconfiguration by the server "
 "administrator, or the server could be having temporary problems. Please try "
@@ -373,7 +368,7 @@ msgstr ""
 "Koøenový adresáø zdroje nebyl nalezen! Toto mù¾e být zpùsobeno ¹patnou "
 "konfigurací nebo server není dostupný. Prosím zkuste to pozdìji."
 
-#: lib/Chora.php:470
+#: lib/Chora.php:472
 msgid "Statistics"
 msgstr "Statistiky"
 
@@ -382,7 +377,7 @@ msgstr "Statistiky"
 msgid "Statistics for %s"
 msgstr "Statistiky pro %s"
 
-#: templates/log/rev.inc:34
+#: templates/log/rev.inc:32
 msgid "Tags:"
 msgstr "Znaèky:"
 
@@ -428,7 +423,7 @@ msgstr "Zobrazit V
 msgid "View revisions on:"
 msgstr "Zobrazit revize z:"
 
-#: lib/Chora.php:472
+#: lib/Chora.php:474
 msgid "View:"
 msgstr "Zobrazit:"
 
@@ -436,11 +431,11 @@ msgstr "Zobrazit:"
 msgid "_Author"
 msgstr "_Autor"
 
-#: lib/Chora.php:465
+#: lib/Chora.php:467
 msgid "_Branches"
 msgstr "_Vìtve"
 
-#: lib/Chora.php:439
+#: lib/Chora.php:441
 msgid "_Browse"
 msgstr "_Prohlí¾et"
 
@@ -448,15 +443,15 @@ msgstr "_Prohl
 msgid "_File"
 msgstr "_Soubor"
 
-#: lib/Chora.php:468
+#: lib/Chora.php:470
 msgid "_Graph"
 msgstr "_Graf"
 
-#: lib/Chora.php:459
+#: lib/Chora.php:461
 msgid "_Logs"
 msgstr "_Záznamy"
 
-#: lib/Chora.php:462
+#: lib/Chora.php:464
 msgid "_Patchsets"
 msgstr "_Patchsets"
 
@@ -464,7 +459,7 @@ msgstr "_Patchsets"
 msgid "_Rev"
 msgstr "_Rev"
 
-#: lib/Chora.php:470
+#: lib/Chora.php:472
 msgid "_Statistics"
 msgstr "_Statistiky"
 
@@ -472,7 +467,7 @@ msgstr "_Statistiky"
 msgid "and:"
 msgstr "a:"
 
-#: templates/history/rev.inc:4 templates/log/rev.inc:5
+#: templates/log/rev.inc:5 templates/history/rev.inc:4
 #, php-format
 msgid "by %s"
 msgstr "podle %s"
@@ -481,11 +476,11 @@ msgstr "podle %s"
 msgid "changed lines"
 msgstr "zmìnìné øádky"
 
-#: lib/Chora.php:31
+#: lib/Chora.php:32
 msgid "day"
 msgstr "den"
 
-#: lib/Chora.php:31
+#: lib/Chora.php:32
 msgid "days"
 msgstr "dnù"
 
@@ -493,27 +488,27 @@ msgstr "dn
 msgid "for further information."
 msgstr "pro dal¹í informace."
 
-#: lib/Chora.php:30
+#: lib/Chora.php:31
 msgid "hour"
 msgstr "hodina"
 
-#: lib/Chora.php:30
+#: lib/Chora.php:31
 msgid "hours"
 msgstr "hodin"
 
-#: lib/Chora.php:29
+#: lib/Chora.php:30
 msgid "minute"
 msgstr "minuta"
 
-#: lib/Chora.php:29
+#: lib/Chora.php:30
 msgid "minutes"
 msgstr "minuty"
 
-#: lib/Chora.php:33
+#: lib/Chora.php:34
 msgid "month"
 msgstr "mìsíc"
 
-#: lib/Chora.php:33
+#: lib/Chora.php:34
 msgid "months"
 msgstr "mìsícù"
 
@@ -522,11 +517,11 @@ msgstr "m
 msgid "revision %s"
 msgstr "revize %s"
 
-#: lib/Chora.php:28
+#: lib/Chora.php:29
 msgid "second"
 msgstr "sekunda"
 
-#: lib/Chora.php:28
+#: lib/Chora.php:29
 msgid "seconds"
 msgstr "sekundy"
 
@@ -535,22 +530,22 @@ msgstr "sekundy"
 msgid "version %s"
 msgstr "verze %s"
 
-#: lib/Chora.php:42
+#: lib/Chora.php:43
 msgid "very little time"
 msgstr "pøed chvílí"
 
-#: lib/Chora.php:32
+#: lib/Chora.php:33
 msgid "week"
 msgstr "týden"
 
-#: lib/Chora.php:32
+#: lib/Chora.php:33
 msgid "weeks"
 msgstr "týdny"
 
-#: lib/Chora.php:34
+#: lib/Chora.php:35
 msgid "year"
 msgstr "rok"
 
-#: lib/Chora.php:34
+#: lib/Chora.php:35
 msgid "years"
 msgstr "rokù"
diff --git a/po/da_DK.po b/po/da_DK.po
index 3bcbb13..12a1155 100644
--- a/po/da_DK.po
+++ b/po/da_DK.po
@@ -1,15 +1,15 @@
 # Danish translations for Chora package
 # Danske oversættelser for pakke Chora.
-# Copyright (C) 2006 Horde Project
-# This file is distributed under the same license as the FRAMEWORK package.
+# Copyright 2006-2009 The Horde Project
+# This file is distributed under the same license as the Chora package.
 # Brian Truelsen <horde+i18n at briantruelsen.dk>, 2006.
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: Chora H3 (2.0.2-cvs)\n"
+"Project-Id-Version: Chora H3 (2.1-cvs)\n"
 "Report-Msgid-Bugs-To: dev at lists.horde.org\n"
-"POT-Creation-Date: 2006-03-06 12:47+0100\n"
-"PO-Revision-Date: 2006-03-11 17:23+0100\n"
+"POT-Creation-Date: 2006-03-05 18:16+0100\n"
+"PO-Revision-Date: 2006-03-11 15:39+0100\n"
 "Last-Translator: Brian Truelsen <horde+i18n at briantruelsen.dk>\n"
 "Language-Team: i18n at lists.horde.org\n"
 "MIME-Version: 1.0\n"
@@ -17,11 +17,12 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
-#: templates/annotate/header.inc:4
+#: templates/annotate/header.inc:3
 msgid "#"
 msgstr "#"
 
 #: templates/patchsets/ps.inc:12 templates/log/rev.inc:26
+#: templates/annotate/footer.inc:22
 #, php-format
 msgid "%s ago"
 msgstr "%s siden"
@@ -39,8 +40,8 @@ msgstr "Alle forgreninger"
 msgid "Annotate"
 msgstr "Annotér"
 
-#: templates/stats/stats.inc:3 templates/directory/header.inc:18
-#: templates/annotate/header.inc:5
+#: templates/stats/stats.inc:3 templates/directory/header.inc:12
+#: templates/annotate/header.inc:4
 msgid "Author"
 msgstr "Forfatter"
 
@@ -57,7 +58,7 @@ msgstr "Forgreningspunkt for:"
 msgid "Branch:"
 msgstr "Forgrening:"
 
-#: lib/Chora.php:465
+#: lib/Chora.php:455
 msgid "Branches"
 msgstr "Forgreninger"
 
@@ -65,12 +66,7 @@ msgstr "Forgreninger"
 msgid "Branching to"
 msgstr "Forgrén til"
 
-#: templates/checkout/checkout.inc:35
-#, php-format
-msgid "Changed since <b>%s</b>"
-msgstr "Ændret siden <b>%s</b>"
-
-#: templates/log/rev.inc:37
+#: templates/log/rev.inc:37 templates/checkout/checkout.inc:35
 #, php-format
 msgid "Changed since <strong>%s</strong>"
 msgstr "Ændret siden <strong>%s</strong>"
@@ -90,7 +86,7 @@ msgid ""
 "Click on the links in between revisions to get a diff between those "
 "revisions."
 msgstr ""
-"Klik på henvisningerne mellem revisionerne for at få en diff mellem disse "
+"Klik på henvisningen mellem revisioner for at få en diff mellem disse "
 "revisioner."
 
 #: templates/cvsgraph/cvsgraph.inc:10
@@ -117,11 +113,11 @@ msgstr "Sammenh
 msgid "Customize tasks to run upon logging in to Chora."
 msgstr "Tilpas opgaver, der bliver udført, når du logger ind i Chora."
 
-#: templates/directory/header.inc:23
+#: templates/directory/header.inc:15
 msgid "Da_te"
 msgstr "Dato"
 
-#: templates/directory/header.inc:23
+#: templates/directory/header.inc:15
 msgid "Date"
 msgstr "Dato"
 
@@ -164,7 +160,7 @@ msgstr "Diff til version %s"
 msgid "Directory"
 msgstr "Katalog"
 
-#: annotate.php:30 co.php:73 templates/log/rev.inc:10
+#: annotate.php:31 co.php:73 templates/log/rev.inc:10
 msgid "Download"
 msgstr "Hent"
 
@@ -178,10 +174,10 @@ msgstr "Fejl"
 
 #: templates/error_page.inc:7
 msgid "Error Encountered"
-msgstr "Fejl påtruffet"
+msgstr "Fejl optrådt"
 
 #: templates/directory/file.inc:7 templates/directory/file.inc:19
-#: templates/directory/header.inc:8
+#: templates/directory/header.inc:6
 msgid "File"
 msgstr "Fil"
 
@@ -193,7 +189,7 @@ msgstr "Filer 
 msgid "Get Diffs"
 msgstr "Hent diff'er"
 
-#: lib/Chora.php:468
+#: lib/Chora.php:458
 msgid "Graph"
 msgstr "Graf"
 
@@ -214,7 +210,7 @@ msgstr "Skjul slettede filer"
 msgid "Human Readable"
 msgstr "Læsbart"
 
-#: templates/directory/header.inc:25
+#: templates/directory/header.inc:17
 msgid "Last Log"
 msgstr "Sidste logfil"
 
@@ -227,7 +223,7 @@ msgstr "Sidste log-besked for rev %s:"
 msgid "Legend:"
 msgstr "Forklaring:"
 
-#: templates/annotate/header.inc:7
+#: templates/annotate/header.inc:6
 msgid "Line"
 msgstr "Linie"
 
@@ -248,7 +244,7 @@ msgstr "Log:"
 msgid "Login Tasks"
 msgstr "Login-opgaver"
 
-#: lib/Chora.php:459
+#: lib/Chora.php:449
 msgid "Logs"
 msgstr "Logfiler"
 
@@ -276,7 +272,7 @@ msgstr "NoWhitespaceChanges"
 msgid "Other Options"
 msgstr "Andre indstillinger"
 
-#: lib/Chora.php:368
+#: lib/Chora.php:354
 msgid "Other Repositories"
 msgstr "Andre arkiver"
 
@@ -289,7 +285,7 @@ msgstr "For
 msgid "PatchSet %s</span> by %s"
 msgstr "Lappegrej %s</span> af %s"
 
-#: lib/Chora.php:462
+#: lib/Chora.php:452
 msgid "Patchsets"
 msgstr "Lappegrejer"
 
@@ -307,11 +303,15 @@ msgstr "Kontakt"
 msgid "Removed in v.%s"
 msgstr "Slette i v.%s"
 
+#: lib/api.php:30
+msgid "Repositories"
+msgstr "Arkiver"
+
 #: templates/log/request.inc:14
 msgid "Retrieve diffs between:"
-msgstr "Hent diff'er mellem:"
+msgstr "Fremskaf diff'er mellem:"
 
-#: templates/directory/header.inc:13 templates/annotate/header.inc:6
+#: templates/directory/header.inc:9 templates/annotate/header.inc:5
 msgid "Rev"
 msgstr "Rev"
 
@@ -322,8 +322,8 @@ msgstr "Revision %s for fil %s ikke fundet."
 
 #: templates/checkout/checkout.inc:4
 #, php-format
-msgid "Revision <b>%s</b>, <i>%s</i> (%s ago) by %s"
-msgstr "Revision <b>%s</b>, <i>%s</i> (%s siden) af %s"
+msgid "Revision <strong>%s</strong>, <em>%s</em> (%s ago) by %s"
+msgstr "Revision <strong>%s</strong>, <em>%s</em> (%s siden) af %s"
 
 #: templates/log/rev.inc:15
 msgid "Select for Diff"
@@ -341,12 +341,7 @@ msgstr "Vis slettede filer"
 msgid "Side-by-Side"
 msgstr "Side-om-side"
 
-#: templates/directory/header.inc:7 templates/directory/header.inc:12
-#: templates/directory/header.inc:17 templates/directory/header.inc:22
-msgid "Sort Order"
-msgstr "Sorteringsrækkefølge"
-
-#: annotate.php:27
+#: annotate.php:28
 #, php-format
 msgid "Source Annotation of %s for version %s"
 msgstr "Kilde-annotering af %s for version %s"
@@ -354,29 +349,29 @@ msgstr "Kilde-annotering af %s for version %s"
 #: history.php:160
 #, php-format
 msgid "Source Branching View for %s"
-msgstr "Kildeforgreningsvisning af %s"
+msgstr "Kildeforgreningsvisning for %s"
 
 #: browse.php:32
 #, php-format
 msgid "Source Directory of /%s"
 msgstr "Kildekatalog af /%s"
 
-#: browse.php:126
+#: browse.php:125
 #, php-format
 msgid "Source Log for %s"
 msgstr "Kildelog af %s"
 
-#: lib/Chora.php:182
+#: lib/Chora.php:175
 msgid ""
 "SourceRoot not found! This could be a misconfiguration by the server "
 "administrator, or the server could be having temporary problems. Please try "
 "again later."
 msgstr ""
-"Kilderod ikke fundet! Dette kan skyldes en fejlkonfiguration af server-"
-"administratoren, eller serveren kan have midlertidige problemer. Prøv igen "
-"senere."
+"Kilderod ikke fundet! Dette kan være en fejlkonfiguration af "
+"serveradministratoren, eller serveren kan være ude for midlertidige "
+"problemer. Prøv igen senere."
 
-#: lib/Chora.php:470
+#: lib/Chora.php:460
 msgid "Statistics"
 msgstr "Statistikker"
 
@@ -395,10 +390,10 @@ msgid ""
 "You may select a symbolic revision name using the selection box or you may "
 "type in a numeric name using the type-in text box."
 msgstr ""
-"Dette skema tillader dig at anmode om diff'er mellem vilkårligt to "
-"revisioner af en fil. Du kan vælge et symbolsk revisionsnavn med "
-"selektionsværktøjet eller du kan indtaste et numerisk navn ved brug af "
-"indtastningsfeltet. "
+"Dette skema tillader dig at anmode om diff'er mellem vilkårlige, to "
+"revisioner af en fil. Du kan vælge et symbolsk revisionsnavn ved brug af "
+"valg-kasserne eller du kan indtaste et numerisk navn ved brug af tekst-"
+"indtastningsfeltet."
 
 #: templates/headerbar.inc:16
 msgid "Tracking Branch"
@@ -414,13 +409,13 @@ msgstr "Sammensmeltet"
 
 #: templates/log/request.inc:18 templates/log/request.inc:38
 msgid "Use Text Field"
-msgstr "Anvend tekst-felt"
+msgstr "Brug tekst-felt"
 
 #: config/prefs.php.dist:37
 msgid "Use last viewed file or directory at login time"
-msgstr "Anvend sidst besete fil eller katalog ved logind."
+msgstr "Brug sidst sete fil eller katalog ved logind."
 
-#: annotate.php:29 templates/log/rev.inc:9
+#: annotate.php:30 templates/log/rev.inc:9
 msgid "View"
 msgstr "Se"
 
@@ -432,43 +427,43 @@ msgstr "Se gren"
 msgid "View revisions on:"
 msgstr "Se revisioner på:"
 
-#: lib/Chora.php:472
+#: lib/Chora.php:462
 msgid "View:"
 msgstr "Se:"
 
-#: templates/directory/header.inc:18
+#: templates/directory/header.inc:12
 msgid "_Author"
 msgstr "Forfatter"
 
-#: lib/Chora.php:465
+#: lib/Chora.php:455
 msgid "_Branches"
 msgstr "Foregreninger"
 
-#: lib/Chora.php:439
+#: lib/Chora.php:433
 msgid "_Browse"
 msgstr "Skim"
 
-#: templates/directory/header.inc:8
+#: templates/directory/header.inc:6
 msgid "_File"
 msgstr "Fil"
 
-#: lib/Chora.php:468
+#: lib/Chora.php:458
 msgid "_Graph"
 msgstr "Graf"
 
-#: lib/Chora.php:459
+#: lib/Chora.php:449
 msgid "_Logs"
 msgstr "Logninger"
 
-#: lib/Chora.php:462
+#: lib/Chora.php:452
 msgid "_Patchsets"
 msgstr "Lappesæt"
 
-#: templates/directory/header.inc:13
+#: templates/directory/header.inc:9
 msgid "_Rev"
 msgstr "Rev"
 
-#: lib/Chora.php:470
+#: lib/Chora.php:460
 msgid "_Statistics"
 msgstr "Statistikker"
 
diff --git a/po/de_DE.po b/po/de_DE.po
index 9a530cd..0698768 100644
--- a/po/de_DE.po
+++ b/po/de_DE.po
@@ -1,12 +1,13 @@
-# Chora German translation.
-# Copyright (C) 2001-2005 Jan Schneider.
-# Jan Schneider <jan at horde.org>, 2001-2005.
+# German translation for Chora.
+# Copyright 2001-2009 The Horde Project
+# This file is distributed under the same license as the Chora package.
+# Jan Schneider <jan at horde.org>, 2001-2008.
 msgid ""
 msgstr ""
-"Project-Id-Version: Chora 2.0-cvs\n"
+"Project-Id-Version: Chora 3.0-cvs\n"
 "Report-Msgid-Bugs-To: dev at lists.horde.org\n"
-"POT-Creation-Date: 2006-03-06 12:47+0100\n"
-"PO-Revision-Date: 2005-07-06 01:26+0200\n"
+"POT-Creation-Date: 2008-09-18 11:24+0200\n"
+"PO-Revision-Date: 2008-04-01 15:05+0200\n"
 "Last-Translator: Jan Schneider <jan at horde.org>\n"
 "Language-Team: German <dev at lists.horde.org>\n"
 "MIME-Version: 1.0\n"
@@ -17,43 +18,57 @@ msgstr ""
 msgid "#"
 msgstr "Nr."
 
-#: templates/patchsets/ps.inc:12 templates/log/rev.inc:26
+#: templates/log/header.inc:17
+#, php-format
+msgid ""
+"%1$s shows diffs to the previous revision. If you select a revision by "
+"clicking its row, %1$s will show the differences to the selected row."
+msgstr ""
+"%1$s zeigt die Unterschiede zur vorigen Version an. Wenn Sie eine Version "
+"durch Klicken auf deren Zeile ausgewählt haben, zeigt %1$s die Unterschiede "
+"zu der ausgewählten Version an."
+
+#: co.php:84
+#, php-format
+msgid "%s Revision %s (%s ago)"
+msgstr "%s Version %s (vor %s)"
+
+#: templates/annotate/footer.inc:22
 #, php-format
 msgid "%s ago"
 msgstr "vor %s"
 
-#: templates/diff/hr/footer.inc:19
-#, php-format
-msgid "Added in v.%s"
-msgstr "hinzugefügt in v.%s"
+#: templates/diff/hr/header.inc:25
+msgid "Added"
+msgstr "Hinzugefügt"
 
-#: templates/log/request.inc:57
+#: templates/log/header.inc:31
 msgid "All Branches"
 msgstr "Alle Branches"
 
-#: co.php:72 templates/log/rev.inc:8
+#: co.php:89
 msgid "Annotate"
 msgstr "Kommentiert"
 
-#: templates/directory/header.inc:18 templates/stats/stats.inc:3
-#: templates/annotate/header.inc:5
+#: templates/directory/header.inc:12 templates/patchsets/header.inc:15
+#: templates/stats/stats.inc:4 templates/annotate/header.inc:5
+#: templates/log/header.inc:49
 msgid "Author"
 msgstr "Autor"
 
-#: templates/directory/back.inc:3
+#: templates/checkout/checkout.inc:4 templates/diff/hr/header.inc:35
+msgid "Author:"
+msgstr "Autor:"
+
+#: templates/directory/back.inc:4
 msgid "Back"
 msgstr "Zurück"
 
-#: templates/checkout/checkout.inc:27 templates/log/rev.inc:31
-msgid "Branch Point for:"
-msgstr "Branch Stelle für:"
-
-#: templates/checkout/checkout.inc:11 templates/patchsets/ps.inc:14
-#: templates/log/rev.inc:28
+#: templates/checkout/checkout.inc:5 templates/diff/hr/header.inc:37
 msgid "Branch:"
 msgstr "Branch:"
 
-#: lib/Chora.php:465
+#: lib/Chora.php:502 lib/Chora.php:503 lib/Chora.php:507 lib/Chora.php:508
 msgid "Branches"
 msgstr "Branches"
 
@@ -61,27 +76,20 @@ msgstr "Branches"
 msgid "Branching to"
 msgstr "Branching nach"
 
-#: templates/checkout/checkout.inc:35
-#, php-format
-msgid "Changed since <b>%s</b>"
-msgstr "Geändert seit <b>%s</b>"
-
-#: templates/log/rev.inc:37
-#, php-format
-msgid "Changed since <strong>%s</strong>"
-msgstr "Geändert seit <strong>%s</strong>"
+#: lib/Chora.php:392
+msgid "Change repositories:"
+msgstr "Repository ändern:"
 
-#: templates/history/rev.inc:8
+#: templates/history/rev.inc:7
 #, php-format
 msgid "Changed: %s"
 msgstr "Geändert: %s"
 
-#: co.php:70
-#, php-format
-msgid "Checkout of %s (revision %s)"
-msgstr "Checkout von %s (Version %s)"
+#: templates/checkout/checkout.inc:12
+msgid "Checkout"
+msgstr "Checkout"
 
-#: templates/cvsgraph/cvsgraph.inc:11
+#: templates/cvsgraph/cvsgraph.inc:5
 msgid ""
 "Click on the links in between revisions to get a diff between those "
 "revisions."
@@ -89,23 +97,19 @@ msgstr ""
 "Klicken Sie auf die Links zwischen den Versionen, um den Unterschied "
 "zwischen diesen Versionen anzuzeigen."
 
-#: templates/cvsgraph/cvsgraph.inc:10
+#: templates/cvsgraph/cvsgraph.inc:4
 msgid "Click on the revisions and branches to display the file."
 msgstr "Klicken Sie auf die Versionen oder Branches, um die Datei anzuzeigen."
 
-#: templates/log/rev.inc:42 templates/log/rev.inc:50
-msgid "Colored"
-msgstr "Farbig"
-
-#: templates/checkout/checkout.inc:19 templates/patchsets/ps.inc:17
-msgid "Commit Tags:"
-msgstr "Commit Tags:"
+#: templates/diff/hr/header.inc:13
+msgid "Column"
+msgstr "Spalte"
 
-#: templates/stats/stats.inc:4
+#: templates/stats/stats.inc:5
 msgid "Commits"
 msgstr "Commits"
 
-#: templates/log/request.inc:28
+#: templates/diff/hr/header.inc:12
 msgid "Context"
 msgstr "Context"
 
@@ -114,58 +118,54 @@ msgid "Customize tasks to run upon logging in to Chora."
 msgstr ""
 "Legen Sie fest, was nach der Anmeldung bei Chora durchgeführt werden soll."
 
-#: templates/directory/header.inc:23
+#: templates/directory/header.inc:15
 msgid "Da_te"
 msgstr "Da_tum"
 
-#: templates/directory/header.inc:23
+#: templates/directory/header.inc:15 templates/patchsets/header.inc:14
+#: templates/log/header.inc:48
 msgid "Date"
 msgstr "Datum"
 
-#: patchsets.php:73
+#: templates/diff/hr/header.inc:36
+msgid "Date:"
+msgstr "Datum:"
+
+#: patchsets.php:79
 msgid "Deleted"
 msgstr "Gelöscht"
 
-#: templates/directory/file.inc:5 templates/directory/file.inc:17
+#: templates/directory/file.inc:5 templates/directory/file.inc:28
 msgid "Deleted File"
 msgstr "Gelöschte Dateien"
 
-#: templates/log/rev.inc:13
-msgid "Deselect"
-msgstr "Auswahl aufheben"
-
-#: patchsets.php:70
+#: patchsets.php:76 templates/log/header.inc:16
 msgid "Diff"
 msgstr "Unterschiede"
 
-#: patchsets.php:53
-msgid "Diff All Files"
-msgstr "Unterschiede aller Dateien"
-
-#: diff.php:69
+#: diff.php:71
 #, php-format
 msgid "Diff for %s between version %s and %s"
 msgstr "Unterschied von %s zwischen den Versionen %s und %s"
 
-#: templates/log/rev.inc:49
-#, php-format
-msgid "Diffs to <strong>%s</strong>"
-msgstr "Unterschiede zu <strong>%s</strong>"
-
-#: templates/log/rev.inc:41
-#, php-format
-msgid "Diffs to version %s"
-msgstr "Unterschiede zur Version %s"
-
 #: templates/directory/dir.inc:4 templates/directory/dir.inc:6
 msgid "Directory"
 msgstr "Verzeichnis"
 
-#: annotate.php:30 co.php:73 templates/log/rev.inc:10
+#: annotate.php:33 co.php:90
 msgid "Download"
 msgstr "Herunterladen"
 
-#: templates/log/request.inc:30
+#: templates/diff/hr/header.inc:9
+msgid "Download diff as: "
+msgstr "Unterschiede herunterladen als: "
+
+#: templates/checkout/checkout.inc:23
+#, php-format
+msgid "Download revision %s"
+msgstr "Version %s herunterladen"
+
+#: templates/diff/hr/header.inc:14
 msgid "Ed Script"
 msgstr "Ed Script"
 
@@ -177,120 +177,108 @@ msgstr "Fehler"
 msgid "Error Encountered"
 msgstr "Ein Fehler ist aufgetreten"
 
-#: templates/directory/file.inc:7 templates/directory/file.inc:19
-#: templates/directory/header.inc:8
+#: templates/directory/file.inc:7 templates/directory/file.inc:30
+#: templates/directory/header.inc:6
 msgid "File"
 msgstr "Datei"
 
-#: templates/patchsets/ps.inc:21
-msgid "Files Changed:"
-msgstr "Geänderte Dateien:"
-
-#: templates/log/request.inc:44
-msgid "Get Diffs"
-msgstr "Unterschiede anzeigen"
+#: templates/patchsets/header.inc:16
+msgid "Files"
+msgstr "Dateien"
 
-#: lib/Chora.php:468
-msgid "Graph"
-msgstr "Grafik"
+#: templates/diff/hr/header.inc:16
+msgid "Get Diff"
+msgstr "Unterschiede herunterladen"
 
-#: cvsgraph.php:48
+#: cvsgraph.php:51
 #, php-format
 msgid "Graph for %s"
 msgstr "Grafik für %s"
 
-#: browse.php:40
+#: browse.php:42
 msgid "Hide Deleted Files"
 msgstr "Gelöschte Dateien verstecken"
 
-#: browse.php:40
+#: browse.php:44
 msgid "Hide _Deleted Files"
 msgstr "_Gelöschte Dateien verstecken"
 
-#: templates/log/request.inc:26
-msgid "Human Readable"
-msgstr "Human Readable"
-
-#: templates/directory/header.inc:25
+#: templates/directory/header.inc:17
 msgid "Last Log"
 msgstr "Letzter Eintrag"
 
-#: templates/diff/hr/header.inc:5
-#, php-format
-msgid "Last Log Message for rev %s:"
-msgstr "Letzter Log-Eintrag für rev %s:"
-
-#: templates/diff/hr/footer.inc:5
-msgid "Legend:"
-msgstr "Legende:"
-
-#: templates/annotate/header.inc:7
+#: templates/annotate/header.inc:8
 msgid "Line"
 msgstr "Zeile"
 
-#: templates/diff/hr/row.inc:3 templates/diff/hr/row.inc:7
+#: templates/diff/hr/row.inc:3 templates/diff/hr/row.inc:6
 #, php-format
 msgid "Line %s"
 msgstr "Zeile %s"
 
-#: templates/headerbar.inc:11
+#: templates/headerbar.inc:5
 msgid "Location:"
 msgstr "Position:"
 
-#: templates/checkout/checkout.inc:42
-msgid "Log:"
-msgstr "Log:"
+#: templates/checkout/checkout.inc:1 templates/patchsets/header.inc:17
+#: templates/diff/hr/header.inc:30 templates/log/header.inc:50
+msgid "Log Message"
+msgstr "Änderungseintrag"
 
 #: config/prefs.php.dist:10
 msgid "Login Tasks"
 msgstr "Anmeldeaufgaben"
 
-#: lib/Chora.php:459
+#: lib/Chora.php:488 lib/Chora.php:489
 msgid "Logs"
 msgstr "Logs"
 
-#: templates/log/rev.inc:43 templates/log/rev.inc:51
-msgid "Long"
-msgstr "Lang"
-
-#: diff.php:61
+#: diff.php:44
 msgid "Malformed Query"
 msgstr "Ungültige Abfrage"
 
-#: patchsets.php:66
+#: lib/Block/tree_menu.php:3
+msgid "Menu List"
+msgstr "Menüliste"
+
+#: templates/diff/hr/header.inc:26
+msgid "Modified"
+msgstr "Geändert"
+
+#: patchsets.php:72
 msgid "New File"
 msgstr "Neue Datei"
 
-#: templates/diff/hr/nochange.inc:6
-msgid "No Viewable Change"
-msgstr "Keine sichtbare Änderung"
+#: templates/diff/hr/nochange.inc:3
+msgid "No Visible Changes"
+msgstr "Keine sichtbaren Änderungen"
 
-#: templates/log/rev.inc:44 templates/log/rev.inc:52
-msgid "NoWhitespaceChanges"
-msgstr "Ohne Leerzeichen-Änderungen"
+#: templates/log/footer.inc:8
+#, php-format
+msgid ""
+"Only showing the 100 latest revisions. %sShow all revisions?</a> (may take a "
+"while)."
+msgstr ""
+"Nur die letzten 100 Versionen werden angezeigt. %sAlle Versionen anzeigen?</"
+"a> (kann eine Weile dauern)."
 
 #: config/prefs.php.dist:9
 msgid "Other Options"
 msgstr "Andere Einstellungen"
 
-#: lib/Chora.php:368
-msgid "Other Repositories"
-msgstr "Andere Repositories"
-
-#: templates/directory/back.inc:3
+#: templates/directory/back.inc:4
 msgid "Parent Directory"
 msgstr "Verzeichnis hoch"
 
-#: templates/patchsets/ps.inc:6
-#, php-format
-msgid "PatchSet %s</span> by %s"
-msgstr "Patchsatz %s</span> von %s"
+#: templates/patchsets/header.inc:13
+msgid "Patchset"
+msgstr "Patchsatz"
 
-#: lib/Chora.php:462
+#: lib/Chora.php:495 lib/Chora.php:496
 msgid "Patchsets"
 msgstr "Patchsätze"
 
-#: patchsets.php:32
+#: patchsets.php:35
 #, php-format
 msgid "Patchsets for %s"
 msgstr "Patchsätze für %s"
@@ -299,71 +287,84 @@ msgstr "Patchs
 msgid "Please contact"
 msgstr "Bitte kontaktieren Sie"
 
-#: templates/diff/hr/footer.inc:11
-#, php-format
-msgid "Removed in v.%s"
-msgstr "gelöscht aus v.%s"
+#: templates/annotate/header.inc:7
+msgid "Prev"
+msgstr "Zurück"
+
+#: templates/diff/hr/header.inc:27
+msgid "Removed"
+msgstr "Entfernt"
 
-#: templates/log/request.inc:14
-msgid "Retrieve diffs between:"
-msgstr "Unterschiede anzeigen zwischen:"
+#: lib/api.php:30
+msgid "Repositories"
+msgstr "Repositories"
 
-#: templates/directory/header.inc:13 templates/annotate/header.inc:6
+#: templates/directory/header.inc:9 templates/annotate/header.inc:6
 msgid "Rev"
 msgstr "Rev"
 
-#: co.php:77
-#, php-format
-msgid "Revision %s for file %s not found."
-msgstr "Version %s der Datei %s nicht gefunden."
+#: templates/log/header.inc:47
+msgid "Revision"
+msgstr "Version"
 
-#: templates/checkout/checkout.inc:4
+#: browse.php:157
 #, php-format
-msgid "Revision <b>%s</b>, <i>%s</i> (%s ago) by %s"
-msgstr "Version <b>%s</b>, <i>%s</i> (vor %s) von %s"
+msgid "Revisions for %s"
+msgstr "Versionen für %s"
+
+#: templates/patchsets/header.inc:4
+msgid "Search Patchsets:"
+msgstr "Patchsätze suchen:"
+
+#: templates/log/header.inc:4
+msgid "Search Revisions:"
+msgstr "Versionen suchen:"
 
-#: templates/log/rev.inc:15
-msgid "Select for Diff"
-msgstr "Für das Anzeigen von Unterschieden auswählen"
+#: templates/log/header.inc:29
+msgid "Show Branch:"
+msgstr "Branch anzeigen:"
 
 #: browse.php:42
 msgid "Show Deleted Files"
 msgstr "Gelöschte Dateien anzeigen"
 
-#: browse.php:42
+#: templates/log/header.inc:18
+msgid "Show Differences"
+msgstr "Unterschiede anzeigen"
+
+#: browse.php:44
 msgid "Show _Deleted Files"
 msgstr "_Gelöschte Dateien anzeigen"
 
-#: templates/log/request.inc:29
-msgid "Side-by-Side"
-msgstr "Side-by-Side"
+#: templates/log/rev.inc:4
+msgid "Show changes to the previous revision"
+msgstr "Änderungen zur vorigen Version anzeigen"
+
+#: templates/log/rev.inc:6
+msgid "Show changes to the selected revision"
+msgstr "Änderungen zur ausgewählten Version anzeigen"
 
-#: templates/directory/header.inc:7 templates/directory/header.inc:12
-#: templates/directory/header.inc:17 templates/directory/header.inc:22
-msgid "Sort Order"
-msgstr "Sortierreihenfolge"
+#: templates/log/header.inc:13
+#, php-format
+msgid "Show diffs between %s and %s"
+msgstr "Unterschiede zwischen %s und %s anzeigen"
 
-#: annotate.php:27
+#: annotate.php:30
 #, php-format
-msgid "Source Annotation of %s for version %s"
-msgstr "Quell-Kommentar von %s für version %s"
+msgid "Source Annotation of %s (revision %s)"
+msgstr "Quell-Kommentar von %s (Version %s)"
 
-#: history.php:160
+#: history.php:163
 #, php-format
 msgid "Source Branching View for %s"
 msgstr "Branch Ansicht für %s"
 
-#: browse.php:32
+#: browse.php:35
 #, php-format
 msgid "Source Directory of /%s"
 msgstr "Verzeichnis von /%s"
 
-#: browse.php:126
-#, php-format
-msgid "Source Log for %s"
-msgstr "Eintrag für %s"
-
-#: lib/Chora.php:182
+#: lib/Chora.php:179
 msgid ""
 "SourceRoot not found! This could be a misconfiguration by the server "
 "administrator, or the server could be having temporary problems. Please try "
@@ -373,7 +374,7 @@ msgstr ""
 "durch den Administrator oder vorübergehende Probleme mit dem Server sein. "
 "Bitte versuchen Sie es später noch einmal."
 
-#: lib/Chora.php:470
+#: lib/Chora.php:513 lib/Chora.php:514
 msgid "Statistics"
 msgstr "Statistiken"
 
@@ -382,107 +383,81 @@ msgstr "Statistiken"
 msgid "Statistics for %s"
 msgstr "Statistik für %s"
 
-#: templates/log/rev.inc:34
+#: templates/patchsets/ps.inc:22 templates/log/rev.inc:14
+msgid "Tags"
+msgstr "Tags"
+
+#: templates/checkout/checkout.inc:6 templates/diff/hr/header.inc:38
 msgid "Tags:"
 msgstr "Tags:"
 
-#: templates/log/request.inc:5
-msgid ""
-"This form allows you to request diffs between any two revisions of a file.  "
-"You may select a symbolic revision name using the selection box or you may "
-"type in a numeric name using the type-in text box."
-msgstr ""
-"Dieses Formular erlaubt die Anzeige von Unterschieden zwischen zwei "
-"beliebigen Versionen einer Datei. Sie können einen symbolischen "
-"Versionsnamen über das Auswahlfeld auswählen oder eine Versionsnummer in das "
-"Textfeld eingeben."
-
-#: templates/headerbar.inc:16
+#: templates/headerbar.inc:9
 msgid "Tracking Branch"
-msgstr "Branch beobachten"
+msgstr "Angezeigter Branch:"
 
-#: templates/log/request.inc:23
-msgid "Type:"
-msgstr "Typ:"
-
-#: templates/log/request.inc:27
+#: templates/diff/hr/header.inc:11
 msgid "Unified"
 msgstr "Unified"
 
-#: templates/log/request.inc:18 templates/log/request.inc:38
-msgid "Use Text Field"
-msgstr "Eingabe in Textfeld:"
+#: templates/diff/hr/header.inc:24
+msgid "Unmodified"
+msgstr "Keine Änderung"
 
 #: config/prefs.php.dist:37
 msgid "Use last viewed file or directory at login time"
 msgstr ""
 "Zuletzt geöffnete Datei oder Verzeichnis nach der Anmeldung wieder verwenden"
 
-#: annotate.php:29 templates/log/rev.inc:9
+#: templates/diff/hr/header.inc:48 templates/diff/hr/header.inc:49
+#, php-format
+msgid "Version %s"
+msgstr "Version %s"
+
+#: annotate.php:32 templates/log/header.inc:34
 msgid "View"
 msgstr "Anzeigen"
 
-#: templates/log/request.inc:60
-msgid "View Branch"
-msgstr "Branch anzeigen"
-
-#: templates/log/request.inc:54
-msgid "View revisions on:"
-msgstr "Versionen anzeigen an:"
-
-#: lib/Chora.php:472
+#: lib/Chora.php:517
 msgid "View:"
 msgstr "Anzeigen:"
 
-#: templates/directory/header.inc:18
+#: templates/directory/header.inc:12
 msgid "_Author"
 msgstr "_Autor"
 
-#: lib/Chora.php:465
+#: lib/Chora.php:504 lib/Chora.php:509
 msgid "_Branches"
 msgstr "_Branches"
 
-#: lib/Chora.php:439
+#: lib/Chora.php:469
 msgid "_Browse"
 msgstr "_Liste"
 
-#: templates/directory/header.inc:8
+#: templates/directory/header.inc:6
 msgid "_File"
 msgstr "_Datei"
 
-#: lib/Chora.php:468
-msgid "_Graph"
-msgstr "_Grafik"
-
-#: lib/Chora.php:459
+#: lib/Chora.php:490
 msgid "_Logs"
 msgstr "_Logs"
 
-#: lib/Chora.php:462
+#: lib/Chora.php:497
 msgid "_Patchsets"
 msgstr "_Patchsätze"
 
-#: templates/directory/header.inc:13
+#: templates/directory/header.inc:9
 msgid "_Rev"
 msgstr "_Rev"
 
-#: lib/Chora.php:470
+#: lib/Chora.php:515
 msgid "_Statistics"
 msgstr "_Statistiken"
 
-#: templates/log/request.inc:35
-msgid "and:"
-msgstr "und:"
-
-#: templates/history/rev.inc:4 templates/log/rev.inc:5
+#: templates/history/rev.inc:3
 #, php-format
 msgid "by %s"
 msgstr "von %s"
 
-#: templates/diff/hr/footer.inc:15
-msgid "changed lines"
-msgstr "geänderte Zeilen"
-
 #: lib/Chora.php:31
 msgid "day"
 msgstr "Tag"
@@ -532,11 +507,6 @@ msgstr "Sekunde"
 msgid "seconds"
 msgstr "Sekunden"
 
-#: templates/diff/hr/header.inc:13 templates/diff/hr/header.inc:14
-#, php-format
-msgid "version %s"
-msgstr "Version %s"
-
 #: lib/Chora.php:42
 msgid "very little time"
 msgstr "sehr kurze Zeit"
diff --git a/po/es_ES.po b/po/es_ES.po
index c8141ab..9fde2e0 100644
--- a/po/es_ES.po
+++ b/po/es_ES.po
@@ -1,15 +1,15 @@
-# Spanish translations for horde package
-# Traducciones al español para el paquete horde.
-# Copyright (C) 2004 Horde Project
-# This file is distributed under the same license as the horde package.
-# Automatically generated, 2004.
+# Spanish translations for chora package
+# Traducciones al español para el paquete chora.
+# Copyright 2008-2009 The Horde Project
+# This file is distributed under the same license as the chora package.
+# Automatically generated, 2008.
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: Chora 2.0-cvs\n"
+"Project-Id-Version: Chora 2.1-cvs\n"
 "Report-Msgid-Bugs-To: dev at lists.horde.org\n"
-"POT-Creation-Date: 2005-10-13 17:16+0200\n"
-"PO-Revision-Date: 2004-12-12 06:40+100\n"
+"POT-Creation-Date: 2009-02-20 08:18+0100\n"
+"PO-Revision-Date: 2009-02-20 08:18+0100\n"
 "Last-Translator: Manuel P. Ayala <mayala at unex.es>\n"
 "Language-Team: i18n at lists.horde.org\n"
 "MIME-Version: 1.0\n"
@@ -21,43 +21,57 @@ msgstr ""
 msgid "#"
 msgstr "#"
 
-#: templates/patchsets/ps.inc:12 templates/log/rev.inc:26
+#: templates/log/header.inc:17
+#, php-format
+msgid ""
+"%1$s shows diffs to the previous revision. If you select a revision by "
+"clicking its row, %1$s will show the differences to the selected row."
+msgstr ""
+"%1$s muestra las diferencias con la revisión anterior. Si selecciona una "
+"revisión pulsando su fila, %1$s mostrará las diferencias de la fila "
+"seleccionada."
+
+#: co.php:84
+#, php-format
+msgid "%s Revision %s (%s ago)"
+msgstr "%s Revisión %s (hace %s)"
+
+#: templates/annotate/footer.inc:22
 #, php-format
 msgid "%s ago"
 msgstr "hace %s"
 
-#: templates/diff/hr/footer.inc:19
-#, php-format
-msgid "Added in v.%s"
-msgstr "Añadido en la v.%s"
+#: templates/diff/hr/header.inc:25
+msgid "Added"
+msgstr "Añadido"
 
-#: templates/log/request.inc:57
+#: templates/log/header.inc:31
 msgid "All Branches"
 msgstr "Todas las ramas"
 
-#: co.php:72 templates/log/rev.inc:8
+#: co.php:89
 msgid "Annotate"
-msgstr "Anotar"
+msgstr "Anotaciones"
 
-#: templates/directory/header.inc:18 templates/stats/stats.inc:3
+#: templates/patchsets/header.inc:15 templates/directory/header.inc:12
+#: templates/stats/stats.inc:4 templates/log/header.inc:49
 #: templates/annotate/header.inc:5
 msgid "Author"
 msgstr "Autor"
 
+#: templates/diff/hr/header.inc:35 templates/checkout/checkout.inc:4
+msgid "Author:"
+msgstr "Autor:"
+
 #: templates/directory/back.inc:4
 msgid "Back"
 msgstr "Atrás"
 
-#: templates/checkout/checkout.inc:27 templates/log/rev.inc:31
-msgid "Branch Point for:"
-msgstr "Punto de ramificación de:"
-
-#: templates/checkout/checkout.inc:11 templates/patchsets/ps.inc:14
-#: templates/log/rev.inc:28
+#: templates/diff/hr/header.inc:37 templates/checkout/checkout.inc:5
 msgid "Branch:"
 msgstr "Rama:"
 
-#: lib/Chora.php:465
+#: lib/Chora.php:502 lib/Chora.php:503 lib/Chora.php:507 lib/Chora.php:508
 msgid "Branches"
 msgstr "Ramas"
 
@@ -65,27 +79,20 @@ msgstr "Ramas"
 msgid "Branching to"
 msgstr "Ramificando a"
 
-#: templates/checkout/checkout.inc:35
-#, php-format
-msgid "Changed since <b>%s</b>"
-msgstr "Cambiado desde <b>%s</b>"
-
-#: templates/log/rev.inc:37
-#, fuzzy, php-format
-msgid "Changed since <strong>%s</strong>"
-msgstr "Cambiado desde <b>%s</b>"
+#: lib/Chora.php:392
+msgid "Change repositories:"
+msgstr "Cambiar depósitos:"
 
-#: templates/history/rev.inc:8
+#: templates/history/rev.inc:7
 #, php-format
 msgid "Changed: %s"
 msgstr "Cambiado: %s"
 
-#: co.php:70
-#, php-format
-msgid "Checkout of %s (revision %s)"
-msgstr "Inspección del CVS de %s (revisión %s)"
+#: templates/checkout/checkout.inc:12
+msgid "Checkout"
+msgstr "Inspeccionar"
 
-#: templates/cvsgraph/cvsgraph.inc:11
+#: templates/cvsgraph/cvsgraph.inc:5
 msgid ""
 "Click on the links in between revisions to get a diff between those "
 "revisions."
@@ -93,83 +100,75 @@ msgstr ""
 "Haga click sobre los vínculos entre las revisiones para obtener las "
 "diferencias entre dichas revisiones."
 
-#: templates/cvsgraph/cvsgraph.inc:10
+#: templates/cvsgraph/cvsgraph.inc:4
 msgid "Click on the revisions and branches to display the file."
 msgstr "Haga click sobre las revisiones y ramas para mostrar el archivo."
 
-#: templates/log/rev.inc:42 templates/log/rev.inc:50
-msgid "Colored"
-msgstr "Coloreado"
+#: templates/diff/hr/header.inc:13
+msgid "Column"
+msgstr "Columna"
 
-#: templates/checkout/checkout.inc:19 templates/patchsets/ps.inc:17
-msgid "Commit Tags:"
-msgstr "Actualizar etiquetas:"
-
-#: templates/stats/stats.inc:4
+#: templates/stats/stats.inc:5
 msgid "Commits"
 msgstr "Actualizaciones"
 
-#: templates/log/request.inc:28
+#: templates/diff/hr/header.inc:12
 msgid "Context"
 msgstr "Contexto"
 
-#: config/prefs.php.dist:11
+#: config/.bak/prefs.php.dist:11
 msgid "Customize tasks to run upon logging in to Chora."
 msgstr "Personalice las tareas a ejecutar al iniciar sesión en Chora."
 
-#: templates/directory/header.inc:23
+#: templates/directory/header.inc:15
 msgid "Da_te"
 msgstr "_Fecha"
 
-#: templates/directory/header.inc:23
+#: templates/patchsets/header.inc:14 templates/directory/header.inc:15
+#: templates/log/header.inc:48
 msgid "Date"
 msgstr "Fecha"
 
-#: patchsets.php:66
+#: templates/diff/hr/header.inc:36
+msgid "Date:"
+msgstr "Fecha:"
+
+#: patchsets.php:79
 msgid "Deleted"
 msgstr "Eliminado"
 
-#: templates/directory/file.inc:5 templates/directory/file.inc:17
+#: templates/directory/file.inc:5 templates/directory/file.inc:28
 msgid "Deleted File"
 msgstr "Archivo eliminado"
 
-#: templates/log/rev.inc:13
-msgid "Deselect"
-msgstr "Deseleccionado"
-
-#: patchsets.php:63
+#: patchsets.php:76 templates/log/header.inc:16
 msgid "Diff"
 msgstr "Diferencias"
 
-#: patchsets.php:46
-msgid "Diff All Files"
-msgstr "Diferencias de todos los archivos"
-
-#: diff.php:69
+#: diff.php:71
 #, php-format
 msgid "Diff for %s between version %s and %s"
 msgstr "Diferencias de %s entre la versión %s y la %s"
 
-#: templates/log/rev.inc:49
-#, fuzzy, php-format
-msgid "Diffs to <strong>%s</strong>"
-msgstr "Diferencias con <b>%s</b>"
-
-#: templates/log/rev.inc:41
-#, php-format
-msgid "Diffs to version %s"
-msgstr "Diferencias con la versión %s"
-
-#: templates/directory/dir.inc:5 templates/directory/dir.inc:7
+#: templates/directory/dir.inc:4 templates/directory/dir.inc:6
 msgid "Directory"
 msgstr "Directorio"
 
-#: annotate.php:30 co.php:73 templates/log/rev.inc:10
+#: annotate.php:33 co.php:90
 msgid "Download"
 msgstr "Descargar"
 
+#: templates/diff/hr/header.inc:9
+msgid "Download diff as: "
+msgstr "Descargar diff como: "
+
+#: templates/checkout/checkout.inc:23
+#, php-format
+msgid "Download revision %s"
+msgstr "Descargar revisión %s"
+
 # ¿Dónde está?
-#: templates/log/request.inc:30
+#: templates/diff/hr/header.inc:14
 msgid "Ed Script"
 msgstr "Script Ed"
 
@@ -181,120 +180,108 @@ msgstr "Error"
 msgid "Error Encountered"
 msgstr "Se ha encontrado un error"
 
-#: templates/directory/file.inc:7 templates/directory/file.inc:19
-#: templates/directory/header.inc:8
+#: templates/directory/header.inc:6 templates/directory/file.inc:7
+#: templates/directory/file.inc:30
 msgid "File"
 msgstr "Archivo"
 
-#: templates/patchsets/ps.inc:21
-msgid "Files Changed:"
-msgstr "Archivos cambiados:"
+#: templates/patchsets/header.inc:16
+msgid "Files"
+msgstr "Archivos"
 
-#: templates/log/request.inc:44
-msgid "Get Diffs"
+#: templates/diff/hr/header.inc:16
+msgid "Get Diff"
 msgstr "Obtener diferencias"
 
-#: lib/Chora.php:468
-msgid "Graph"
-msgstr "Gráfico"
-
-#: cvsgraph.php:48
+#: cvsgraph.php:51
 #, php-format
 msgid "Graph for %s"
 msgstr "Gráfico de %s"
 
-#: browse.php:40
+#: browse.php:42
 msgid "Hide Deleted Files"
 msgstr "Ocultar archivos eliminados"
 
-#: browse.php:40
+#: browse.php:44
 msgid "Hide _Deleted Files"
 msgstr "_Ocultar archivos eliminados"
 
-#: templates/log/request.inc:26
-msgid "Human Readable"
-msgstr "Legible por las personas"
-
-#: templates/directory/header.inc:25
+#: templates/directory/header.inc:17
 msgid "Last Log"
 msgstr "Último registro"
 
-#: templates/diff/hr/header.inc:5
-#, php-format
-msgid "Last Log Message for rev %s:"
-msgstr "Último mensaje de la revisión %s en el registro:"
-
-#: templates/diff/hr/footer.inc:5
-msgid "Legend:"
-msgstr "Leyenda:"
-
-#: templates/annotate/header.inc:7
+#: templates/annotate/header.inc:8
 msgid "Line"
 msgstr "Línea"
 
-#: templates/diff/hr/row.inc:3 templates/diff/hr/row.inc:7
+#: templates/diff/hr/row.inc:3 templates/diff/hr/row.inc:6
 #, php-format
 msgid "Line %s"
 msgstr "Línea %s"
 
-#: templates/headerbar.inc:11
+#: templates/headerbar.inc:5
 msgid "Location:"
 msgstr "Ubicación:"
 
-#: templates/checkout/checkout.inc:42
-msgid "Log:"
-msgstr "Registro:"
+#: templates/patchsets/header.inc:17 templates/diff/hr/header.inc:30
+#: templates/log/header.inc:50 templates/checkout/checkout.inc:1
+msgid "Log Message"
+msgstr "Mensaje de registro"
 
-#: config/prefs.php.dist:10
+#: config/.bak/prefs.php.dist:10
 msgid "Login Tasks"
 msgstr "Tareas de inicio de sesión"
 
-#: lib/Chora.php:459
+#: lib/Chora.php:488 lib/Chora.php:489
 msgid "Logs"
 msgstr "Registros"
 
-#: templates/log/rev.inc:43 templates/log/rev.inc:51
-msgid "Long"
-msgstr "Largo"
-
-#: diff.php:61
+#: diff.php:44
 msgid "Malformed Query"
 msgstr "Consulta malformada"
 
-#: patchsets.php:59
+#: lib/Block/tree_menu.php:3
+msgid "Menu List"
+msgstr "Lista del menú"
+
+#: templates/diff/hr/header.inc:26
+msgid "Modified"
+msgstr "Modificado"
+
+#: patchsets.php:72
 msgid "New File"
 msgstr "Nuevo archivo"
 
-#: templates/diff/hr/nochange.inc:6
-msgid "No Viewable Change"
+#: templates/diff/hr/nochange.inc:3
+msgid "No Visible Changes"
 msgstr "Sin cambios visibles"
 
-#: templates/log/rev.inc:44 templates/log/rev.inc:52
-msgid "NoWhitespaceChanges"
-msgstr "Sin considerar espacios en blanco"
+#: templates/log/footer.inc:8
+#, php-format
+msgid ""
+"Only showing the 100 latest revisions. %sShow all revisions?</a> (may take a "
+"while)."
+msgstr ""
+"Sólo se muestran las 100 últimas revisiones. %s¿Mostrar todas las revisiones?"
+"</a> (puede llevar un rato)."
 
-#: config/prefs.php.dist:9
+#: config/.bak/prefs.php.dist:9
 msgid "Other Options"
 msgstr "Otras opciones"
 
-#: lib/Chora.php:368
-msgid "Other Repositories"
-msgstr "Otros depósitos"
-
 #: templates/directory/back.inc:4
 msgid "Parent Directory"
 msgstr "Directorio anterior"
 
-#: templates/patchsets/ps.inc:6
-#, php-format
-msgid "PatchSet %s</span> by %s"
-msgstr "Parche %s</span> de %s"
+#: templates/patchsets/header.inc:13
+msgid "Patchset"
+msgstr "Parche"
 
-#: lib/Chora.php:462
+#: lib/Chora.php:495 lib/Chora.php:496
 msgid "Patchsets"
 msgstr "Parches"
 
-#: patchsets.php:27
+#: patchsets.php:35
 #, php-format
 msgid "Patchsets for %s"
 msgstr "Parches de %s"
@@ -303,71 +290,84 @@ msgstr "Parches de %s"
 msgid "Please contact"
 msgstr "Póngase en contacto con"
 
-#: templates/diff/hr/footer.inc:11
-#, php-format
-msgid "Removed in v.%s"
-msgstr "Eliminado en v.%s"
+#: templates/annotate/header.inc:7
+msgid "Prev"
+msgstr "Ant"
+
+#: templates/diff/hr/header.inc:27
+msgid "Removed"
+msgstr "Eliminado"
 
-#: templates/log/request.inc:14
-msgid "Retrieve diffs between:"
-msgstr "Obtener diferencias entre:"
+#: lib/api.php:30
+msgid "Repositories"
+msgstr "Depósitos"
 
-#: templates/directory/header.inc:13 templates/annotate/header.inc:6
+#: templates/directory/header.inc:9 templates/annotate/header.inc:6
 msgid "Rev"
 msgstr "Rev"
 
-#: co.php:77
-#, php-format
-msgid "Revision %s for file %s not found."
-msgstr "No se encontró la revisión %s del archivo %s."
+#: templates/log/header.inc:47
+msgid "Revision"
+msgstr "Revisión"
 
-#: templates/checkout/checkout.inc:4
+#: browse.php:157
 #, php-format
-msgid "Revision <b>%s</b>, <i>%s</i> (%s ago) by %s"
-msgstr "Revisión <b>%s</b>, <i>%s</i> (desde %s) por %s"
+msgid "Revisions for %s"
+msgstr "Revisión de %s"
+
+#: templates/patchsets/header.inc:4
+msgid "Search Patchsets:"
+msgstr "Buscar parches:"
+
+#: templates/log/header.inc:4
+msgid "Search Revisions:"
+msgstr "Buscar revisiones:"
 
-#: templates/log/rev.inc:15
-msgid "Select for Diff"
-msgstr "Seleccione para diferencias"
+#: templates/log/header.inc:29
+msgid "Show Branch:"
+msgstr "Mostrar rama:"
 
 #: browse.php:42
 msgid "Show Deleted Files"
 msgstr "Mostrar archivos eliminados"
 
-#: browse.php:42
+#: templates/log/header.inc:18
+msgid "Show Differences"
+msgstr "Mostrar diferencias"
+
+#: browse.php:44
 msgid "Show _Deleted Files"
 msgstr "_Mostrar archivos eliminados"
 
-#: templates/log/request.inc:29
-msgid "Side-by-Side"
-msgstr "Lado-a-Lado"
+#: templates/log/rev.inc:4
+msgid "Show changes to the previous revision"
+msgstr "Mostrar cambios desde la revisión anterior"
 
-#: templates/directory/header.inc:7 templates/directory/header.inc:12
-#: templates/directory/header.inc:17 templates/directory/header.inc:22
-msgid "Sort Order"
-msgstr "Tipo de ordenación"
+#: templates/log/rev.inc:6
+msgid "Show changes to the selected revision"
+msgstr "Mostrar cambios con respecto a la revisión seleccionada"
+
+#: templates/log/header.inc:13
+#, php-format
+msgid "Show diffs between %s and %s"
+msgstr "Mostrar diferencias entre %s y %s"
 
-#: annotate.php:27
+#: annotate.php:30
 #, php-format
-msgid "Source Annotation of %s for version %s"
-msgstr "Anotación origen de %s, para la versión %s"
+msgid "Source Annotation of %s (revision %s)"
+msgstr "Anotación origen de %s (revisión %s)"
 
-#: history.php:160
+#: history.php:163
 #, php-format
 msgid "Source Branching View for %s"
 msgstr "Vista ramificada origen de %s"
 
-#: browse.php:32
+#: browse.php:35
 #, php-format
 msgid "Source Directory of /%s"
 msgstr "Directorio origen de /%s"
 
-#: browse.php:126
-#, php-format
-msgid "Source Log for %s"
-msgstr "Registro origen de %s"
-
-#: lib/Chora.php:182
+#: lib/Chora.php:179
 msgid ""
 "SourceRoot not found! This could be a misconfiguration by the server "
 "administrator, or the server could be having temporary problems. Please try "
@@ -377,7 +377,7 @@ msgstr ""
 "del administrador del servidor, o que el servidor tenga problemas "
 "temporales. Vuelva a probar más tarde."
 
-#: lib/Chora.php:470
+#: lib/Chora.php:513 lib/Chora.php:514
 msgid "Statistics"
 msgstr "Estadísticas"
 
@@ -386,106 +386,80 @@ msgstr "Estad
 msgid "Statistics for %s"
 msgstr "Estadísticas de %s"
 
-#: templates/log/rev.inc:34
+#: templates/patchsets/ps.inc:22 templates/log/rev.inc:14
+msgid "Tags"
+msgstr "Etiquetas"
+
+#: templates/diff/hr/header.inc:38 templates/checkout/checkout.inc:6
 msgid "Tags:"
 msgstr "Etiquetas:"
 
-#: templates/log/request.inc:5
-msgid ""
-"This form allows you to request diffs between any two revisions of a file.  "
-"You may select a symbolic revision name using the selection box or you may "
-"type in a numeric name using the type-in text box."
-msgstr ""
-" Este formulario le permite solicitar diferencias entre dos revisiones "
-"cualesquiera de un archivo. Puede elegir un nombre de revisión simbólico en "
-"la casilla de selección o puede escribir un nombre numérico en la casilla de "
-"texto."
-
-#: templates/headerbar.inc:16
+#: templates/headerbar.inc:9
 msgid "Tracking Branch"
 msgstr "Rama de seguimiento"
 
-#: templates/log/request.inc:23
-msgid "Type:"
-msgstr "Tipo:"
-
-#: templates/log/request.inc:27
+#: templates/diff/hr/header.inc:11
 msgid "Unified"
 msgstr "Unificado"
 
-#: templates/log/request.inc:18 templates/log/request.inc:38
-msgid "Use Text Field"
-msgstr "Usar campos de texto"
+#: templates/diff/hr/header.inc:24
+msgid "Unmodified"
+msgstr "Sin cambios"
 
-#: config/prefs.php.dist:37
+#: config/.bak/prefs.php.dist:37
 msgid "Use last viewed file or directory at login time"
 msgstr "Usar último archivo o directorio visto al iniciar sesión"
 
-#: annotate.php:29 templates/log/rev.inc:9
+#: templates/diff/hr/header.inc:48 templates/diff/hr/header.inc:49
+#, php-format
+msgid "Version %s"
+msgstr "Versión %s"
+
+#: annotate.php:32 templates/log/header.inc:34
 msgid "View"
 msgstr "Ver"
 
-#: templates/log/request.inc:60
-msgid "View Branch"
-msgstr "Ver rama"
-
-#: templates/log/request.inc:54
-msgid "View revisions on:"
-msgstr "Ver revisiones en:"
-
-#: lib/Chora.php:472
+#: lib/Chora.php:517
 msgid "View:"
 msgstr "Ver:"
 
-#: templates/directory/header.inc:18
+#: templates/directory/header.inc:12
 msgid "_Author"
 msgstr "A_utor"
 
-#: lib/Chora.php:465
+#: lib/Chora.php:504 lib/Chora.php:509
 msgid "_Branches"
 msgstr "_Ramas"
 
-#: lib/Chora.php:439
+#: lib/Chora.php:469
 msgid "_Browse"
 msgstr "_Examinar"
 
-#: templates/directory/header.inc:8
+#: templates/directory/header.inc:6
 msgid "_File"
 msgstr "_Archivo"
 
-#: lib/Chora.php:468
-msgid "_Graph"
-msgstr "_Gráfico"
-
-#: lib/Chora.php:459
+#: lib/Chora.php:490
 msgid "_Logs"
 msgstr "_Registros"
 
-#: lib/Chora.php:462
+#: lib/Chora.php:497
 msgid "_Patchsets"
 msgstr "_Parches"
 
-#: templates/directory/header.inc:13
+#: templates/directory/header.inc:9
 msgid "_Rev"
 msgstr "_Rev"
 
-#: lib/Chora.php:470
+#: lib/Chora.php:515
 msgid "_Statistics"
 msgstr "_Estadísticas"
 
-#: templates/log/request.inc:35
-msgid "and:"
-msgstr "y:"
-
-#: templates/history/rev.inc:4 templates/log/rev.inc:5
+#: templates/history/rev.inc:3
 #, php-format
 msgid "by %s"
 msgstr "por %s"
 
-#: templates/diff/hr/footer.inc:15
-msgid "changed lines"
-msgstr "líneas modificadas"
-
 #: lib/Chora.php:31
 msgid "day"
 msgstr "día"
@@ -535,11 +509,6 @@ msgstr "segundo"
 msgid "seconds"
 msgstr "segundos"
 
-#: templates/diff/hr/header.inc:13 templates/diff/hr/header.inc:14
-#, php-format
-msgid "version %s"
-msgstr "versión %s"
-
 #: lib/Chora.php:42
 msgid "very little time"
 msgstr "muy poco tiempo"
diff --git a/po/fi_FI.po b/po/fi_FI.po
index 883fe40..23d5b68 100644
--- a/po/fi_FI.po
+++ b/po/fi_FI.po
@@ -1,61 +1,76 @@
 # Finnish translation for Chora.
-# Copyright (C)
+# Copyright
 # Tero Matinlassi <terom at iki.fi>, 2002.
-# Leena Heino <liinu at uta.fi>, 2002-2004.
+# Leena Heino <liinu at uta.fi>, 2002-2008.
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: Chora 2.0-cvs\n"
+"Project-Id-Version: Chora 2.1-cvs\n"
 "Report-Msgid-Bugs-To: dev at lists.horde.org\n"
-"POT-Creation-Date: 2006-03-09 10:26+0200\n"
-"PO-Revision-Date: 2006-01-05 12:59+0200\n"
+"POT-Creation-Date: 2008-11-03 12:17+0200\n"
+"PO-Revision-Date: 2008-11-03 12:59+0200\n"
 "Last-Translator: Leena Heino <liinu at uta.fi>\n"
 "Language-Team: Finnish <i18n at lists.horde.org>\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=iso-8859-1\n"
 "Content-Transfer-Encoding: 8-bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
 #: templates/annotate/header.inc:4
 msgid "#"
 msgstr "Nro"
 
-#: templates/patchsets/ps.inc:12 templates/log/rev.inc:26
+#: templates/log/header.inc:17
+#, php-format
+msgid ""
+"%1$s shows diffs to the previous revision. If you select a revision by "
+"clicking its row, %1$s will show the differences to the selected row."
+msgstr ""
+"%1$s näyttää muutokset edelliseen revisioon nähden. Jos haluat valita muun "
+"revision, napsauttamalla haluamaasi riviä, niin %1$s näyttää muutokset "
+"valitun rivin välillä."
+
+#: co.php:84
+#, php-format
+msgid "%s Revision %s (%s ago)"
+msgstr "%s Revisio %s (%s sitten)"
+
+#: templates/annotate/footer.inc:22
 #, php-format
 msgid "%s ago"
 msgstr "%s sitten"
 
-#: templates/diff/hr/footer.inc:19
-#, php-format
-msgid "Added in v.%s"
-msgstr "Lisätty v.%s"
+#: templates/diff/hr/header.inc:25
+msgid "Added"
+msgstr "Lisätty"
 
-#: templates/log/request.inc:57
+#: templates/log/header.inc:31
 msgid "All Branches"
 msgstr "Kaikki haarat"
 
-#: co.php:72 templates/log/rev.inc:8
+#: co.php:89
 msgid "Annotate"
 msgstr "Selitysmerkintä"
 
-#: templates/stats/stats.inc:3 templates/directory/header.inc:18
+#: templates/stats/stats.inc:4 templates/patchsets/header.inc:15
+#: templates/log/header.inc:49 templates/directory/header.inc:12
 #: templates/annotate/header.inc:5
 msgid "Author"
 msgstr "Tekijä"
 
-#: templates/directory/back.inc:3
+#: templates/diff/hr/header.inc:35 templates/checkout/checkout.inc:4
+msgid "Author:"
+msgstr "Tekijä:"
+
+#: templates/directory/back.inc:4
 msgid "Back"
 msgstr "Takaisin"
 
-#: templates/log/rev.inc:31 templates/checkout/checkout.inc:27
-msgid "Branch Point for:"
-msgstr "Haarautumispiste tiedostolle:"
-
-#: templates/patchsets/ps.inc:14 templates/log/rev.inc:28
-#: templates/checkout/checkout.inc:11
+#: templates/diff/hr/header.inc:37 templates/checkout/checkout.inc:5
 msgid "Branch:"
 msgstr "Haara:"
 
-#: lib/Chora.php:465
+#: lib/Chora.php:502 lib/Chora.php:503 lib/Chora.php:507 lib/Chora.php:508
 msgid "Branches"
 msgstr "Haarat"
 
@@ -63,51 +78,40 @@ msgstr "Haarat"
 msgid "Branching to"
 msgstr "Haarautumispiste:"
 
-#: templates/checkout/checkout.inc:35
-#, php-format
-msgid "Changed since <b>%s</b>"
-msgstr "Muutettu alkaen <b>%s</b>"
-
-#: templates/log/rev.inc:37
-#, php-format
-msgid "Changed since <strong>%s</strong>"
-msgstr "Muutettu alkaen <strong>%s</strong>"
+#: lib/Chora.php:392
+msgid "Change repositories:"
+msgstr "Vaihda varastoa:"
 
-#: templates/history/rev.inc:8
+#: templates/history/rev.inc:7
 #, php-format
 msgid "Changed: %s"
 msgstr "Muutettu: %s"
 
-#: co.php:70
-#, php-format
-msgid "Checkout of %s (revision %s)"
-msgstr "Checkout %s:lle (revisio %s)"
+#: templates/checkout/checkout.inc:12
+msgid "Checkout"
+msgstr "Tuo"
 
-#: templates/cvsgraph/cvsgraph.inc:11
+#: templates/cvsgraph/cvsgraph.inc:5
 msgid ""
 "Click on the links in between revisions to get a diff between those "
 "revisions."
 msgstr ""
-"Napsauta linkkiä revisioiden välillä saadaksesi eroavaisuudet näiden kahden "
+"Napsauta linkkia revisioiden välillä saadaksesi eroavaisuudet näiden kahden "
 "revision välillä."
 
-#: templates/cvsgraph/cvsgraph.inc:10
+#: templates/cvsgraph/cvsgraph.inc:4
 msgid "Click on the revisions and branches to display the file."
 msgstr "Napsauta revisiota tai haaraa nähdäksesi tiedoston."
 
-#: templates/log/rev.inc:42 templates/log/rev.inc:50
-msgid "Colored"
-msgstr "Värillinen"
+#: templates/diff/hr/header.inc:13
+msgid "Column"
+msgstr "Sarake"
 
-#: templates/patchsets/ps.inc:17 templates/checkout/checkout.inc:19
-msgid "Commit Tags:"
-msgstr "Kommitointi tagit:"
-
-#: templates/stats/stats.inc:4
+#: templates/stats/stats.inc:5
 msgid "Commits"
 msgstr "Kommitoinnit"
 
-#: templates/log/request.inc:28
+#: templates/diff/hr/header.inc:12
 msgid "Context"
 msgstr "Konteksti"
 
@@ -115,58 +119,54 @@ msgstr "Konteksti"
 msgid "Customize tasks to run upon logging in to Chora."
 msgstr "Aseta Choraan sisäänkirjautumisen yhteydessä tehtävät asiat."
 
-#: templates/directory/header.inc:23
+#: templates/directory/header.inc:15
 msgid "Da_te"
 msgstr "Päi_väys"
 
-#: templates/directory/header.inc:23
+#: templates/patchsets/header.inc:14 templates/log/header.inc:48
+#: templates/directory/header.inc:15
 msgid "Date"
 msgstr "Päiväys"
 
-#: patchsets.php:73
+#: templates/diff/hr/header.inc:36
+msgid "Date:"
+msgstr "Päiväys:"
+
+#: patchsets.php:79
 msgid "Deleted"
 msgstr "Poistettu"
 
-#: templates/directory/file.inc:5 templates/directory/file.inc:17
+#: templates/directory/file.inc:5 templates/directory/file.inc:28
 msgid "Deleted File"
 msgstr "Poistettu tiedosto"
 
-#: templates/log/rev.inc:13
-msgid "Deselect"
-msgstr "Poista valinta"
-
-#: patchsets.php:70
+#: patchsets.php:76 templates/log/header.inc:16
 msgid "Diff"
 msgstr "Muutokset"
 
-#: patchsets.php:53
-msgid "Diff All Files"
-msgstr "Muutokset kaikista tiedostoista"
-
-#: diff.php:69
+#: diff.php:71
 #, php-format
 msgid "Diff for %s between version %s and %s"
 msgstr "Muutokset %s:ssä versioiden %s ja %s välillä"
 
-#: templates/log/rev.inc:49
-#, php-format
-msgid "Diffs to <strong>%s</strong>"
-msgstr "Muutokset nähden <strong>%s</strong>"
-
-#: templates/log/rev.inc:41
-#, php-format
-msgid "Diffs to version %s"
-msgstr "Muutokset versioon %s"
-
 #: templates/directory/dir.inc:4 templates/directory/dir.inc:6
 msgid "Directory"
 msgstr "Hakemisto"
 
-#: annotate.php:30 co.php:73 templates/log/rev.inc:10
+#: annotate.php:33 co.php:90
 msgid "Download"
 msgstr "Tallenna"
 
-#: templates/log/request.inc:30
+#: templates/diff/hr/header.inc:9
+msgid "Download diff as: "
+msgstr "Tallenna diff nimellä:"
+
+#: templates/checkout/checkout.inc:23
+#, php-format
+msgid "Download revision %s"
+msgstr "Lataa revisio %s"
+
+#: templates/diff/hr/header.inc:14
 msgid "Ed Script"
 msgstr "Ed-skripti"
 
@@ -178,120 +178,108 @@ msgstr "Virhe"
 msgid "Error Encountered"
 msgstr "Tapahtui virhe"
 
-#: templates/directory/file.inc:7 templates/directory/file.inc:19
-#: templates/directory/header.inc:8
+#: templates/directory/file.inc:7 templates/directory/file.inc:30
+#: templates/directory/header.inc:6
 msgid "File"
 msgstr "Tiedosto"
 
-#: templates/patchsets/ps.inc:21
-msgid "Files Changed:"
-msgstr "Tiedostoja muuttunut:"
+#: templates/patchsets/header.inc:16
+msgid "Files"
+msgstr "Tiedostot"
 
-#: templates/log/request.inc:44
-msgid "Get Diffs"
+#: templates/diff/hr/header.inc:16
+msgid "Get Diff"
 msgstr "Hae muutokset"
 
-#: lib/Chora.php:468
-msgid "Graph"
-msgstr "Graafi"
-
-#: cvsgraph.php:48
+#: cvsgraph.php:51
 #, php-format
 msgid "Graph for %s"
 msgstr "Graafi %s:lle"
 
-#: browse.php:40
+#: browse.php:42
 msgid "Hide Deleted Files"
 msgstr "Piilota poistetut tiedostot"
 
-#: browse.php:40
+#: browse.php:44
 msgid "Hide _Deleted Files"
 msgstr "Piilota _poistetut tiedostot"
 
-#: templates/log/request.inc:26
-msgid "Human Readable"
-msgstr "Ihmiselle lukukelpoinen"
-
-#: templates/directory/header.inc:25
+#: templates/directory/header.inc:17
 msgid "Last Log"
 msgstr "Viimeisin lokiviesti"
 
-#: templates/diff/hr/header.inc:5
-#, php-format
-msgid "Last Log Message for rev %s:"
-msgstr "Viimeisin lokiviesti revisiolle %s:"
-
-#: templates/diff/hr/footer.inc:5
-msgid "Legend:"
-msgstr "Selite:"
-
-#: templates/annotate/header.inc:7
+#: templates/annotate/header.inc:8
 msgid "Line"
 msgstr "Rivi"
 
-#: templates/diff/hr/row.inc:3 templates/diff/hr/row.inc:7
+#: templates/diff/hr/row.inc:3 templates/diff/hr/row.inc:6
 #, php-format
 msgid "Line %s"
 msgstr "Rivi %s"
 
-#: templates/headerbar.inc:11
+#: templates/headerbar.inc:5
 msgid "Location:"
 msgstr "Paikka:"
 
-#: templates/checkout/checkout.inc:42
-msgid "Log:"
-msgstr "Loki:"
+#: templates/patchsets/header.inc:17 templates/log/header.inc:50
+#: templates/diff/hr/header.inc:30 templates/checkout/checkout.inc:1
+msgid "Log Message"
+msgstr "Kirjausviesti"
 
 #: config/prefs.php.dist:10
 msgid "Login Tasks"
 msgstr "Tehtävät sisäänkirjautumisessa"
 
-#: lib/Chora.php:459
+#: lib/Chora.php:488 lib/Chora.php:489
 msgid "Logs"
 msgstr "Lokit"
 
-#: templates/log/rev.inc:43 templates/log/rev.inc:51
-msgid "Long"
-msgstr "Pitkä"
-
-#: diff.php:61
+#: diff.php:44
 msgid "Malformed Query"
 msgstr "Epäkelpo kysely"
 
-#: patchsets.php:66
+#: lib/Block/tree_menu.php:3
+msgid "Menu List"
+msgstr "Valikkolista"
+
+#: templates/diff/hr/header.inc:26
+msgid "Modified"
+msgstr "Muokattu"
+
+#: patchsets.php:72
 msgid "New File"
 msgstr "Uusi tiedosto"
 
-#: templates/diff/hr/nochange.inc:6
-msgid "No Viewable Change"
-msgstr "Ei katsottavissa olevia muutoksia"
+#: templates/diff/hr/nochange.inc:3
+msgid "No Visible Changes"
+msgstr "Ei näkyviä muutoksia"
 
-#: templates/log/rev.inc:44 templates/log/rev.inc:52
-msgid "NoWhitespaceChanges"
-msgstr "EiVälilyontiMuutoksia"
+#: templates/log/footer.inc:8
+#, php-format
+msgid ""
+"Only showing the 100 latest revisions. %sShow all revisions?</a> (may take a "
+"while)."
+msgstr ""
+"Näytä vain viimeiset 100 revisiota.  %sNäytä kaikki revisiot?</a> (Tämä voi "
+"kestää kauan)."
 
 #: config/prefs.php.dist:9
 msgid "Other Options"
 msgstr "Muut asetukset"
 
-#: lib/Chora.php:368
-msgid "Other Repositories"
-msgstr "Muut varastot"
-
-#: templates/directory/back.inc:3
+#: templates/directory/back.inc:4
 msgid "Parent Directory"
 msgstr "Edellinen hakemisto"
 
-#: templates/patchsets/ps.inc:6
-#, php-format
-msgid "PatchSet %s</span> by %s"
-msgstr "Paikkajoukko %s</span> by %s:llä"
+#: templates/patchsets/header.inc:13
+msgid "Patchset"
+msgstr "Paikkajoukko"
 
-#: lib/Chora.php:462
+#: lib/Chora.php:495 lib/Chora.php:496
 msgid "Patchsets"
-msgstr "Paikkajoukko"
+msgstr "Paikkajoukkot"
 
-#: patchsets.php:32
+#: patchsets.php:35
 #, php-format
 msgid "Patchsets for %s"
 msgstr "Paikkajoukko %s:lle"
@@ -300,71 +288,84 @@ msgstr "Paikkajoukko %s:lle"
 msgid "Please contact"
 msgstr "Ota yhteys"
 
-#: templates/diff/hr/footer.inc:11
-#, php-format
-msgid "Removed in v.%s"
-msgstr "Poistettu versiossa %s"
+#: templates/annotate/header.inc:7
+msgid "Prev"
+msgstr "Edel"
 
-#: templates/log/request.inc:14
-msgid "Retrieve diffs between:"
-msgstr "Hae muutokset välillä:"
+#: templates/diff/hr/header.inc:27
+msgid "Removed"
+msgstr "Poistettu"
+
+#: lib/api.php:30
+msgid "Repositories"
+msgstr "Varastot"
 
-#: templates/directory/header.inc:13 templates/annotate/header.inc:6
+#: templates/directory/header.inc:9 templates/annotate/header.inc:6
 msgid "Rev"
 msgstr "Rev."
 
-#: co.php:77
-#, php-format
-msgid "Revision %s for file %s not found."
-msgstr "Revisiota %s tiedostosta %s ei löytynyt."
+#: templates/log/header.inc:47
+msgid "Revision"
+msgstr "Revisio"
 
-#: templates/checkout/checkout.inc:4
+#: browse.php:157
 #, php-format
-msgid "Revision <b>%s</b>, <i>%s</i> (%s ago) by %s"
-msgstr "Revisio <b>%s</b>, <i>%s</i> (%s sitten) tekijä %s"
+msgid "Revisions for %s"
+msgstr "Revisiot %s:lle"
+
+#: templates/patchsets/header.inc:4
+msgid "Search Patchsets:"
+msgstr "Hae paaikkajoukkosta:"
 
-#: templates/log/rev.inc:15
-msgid "Select for Diff"
-msgstr "Valitse muutosvertailuun"
+#: templates/log/header.inc:4
+msgid "Search Revisions:"
+msgstr "Hae revisioista:"
+
+#: templates/log/header.inc:29
+msgid "Show Branch:"
+msgstr "Näytä Haara:"
 
 #: browse.php:42
 msgid "Show Deleted Files"
 msgstr "Näytä poistetut tiedostot"
 
-#: browse.php:42
+#: templates/log/header.inc:18
+msgid "Show Differences"
+msgstr "Näyt eroavaisuudet"
+
+#: browse.php:44
 msgid "Show _Deleted Files"
 msgstr "Näytä _poistetut tiedostot"
 
-#: templates/log/request.inc:29
-msgid "Side-by-Side"
-msgstr "Vierekkäin"
+#: templates/log/rev.inc:4
+msgid "Show changes to the previous revision"
+msgstr "Näytä muutokset edelliseen revisioon nähden"
+
+#: templates/log/rev.inc:6
+msgid "Show changes to the selected revision"
+msgstr "Näytä muutokset valittujen revisioiden välillä"
 
-#: templates/directory/header.inc:7 templates/directory/header.inc:12
-#: templates/directory/header.inc:17 templates/directory/header.inc:22
-msgid "Sort Order"
-msgstr "Järjestyksen suunta"
+#: templates/log/header.inc:13
+#, php-format
+msgid "Show diffs between %s and %s"
+msgstr "Näytä muutokset versioiden %s ja %s välillä"
 
-#: annotate.php:27
+#: annotate.php:30
 #, php-format
-msgid "Source Annotation of %s for version %s"
-msgstr "Lähdekoodin selitysmerkinnät tiedostolle %s versiolle %s"
+msgid "Source Annotation of %s (revision %s)"
+msgstr "Lähdekoodin selitysmerkinnät %s (revisio %s)"
 
-#: history.php:160
+#: history.php:163
 #, php-format
 msgid "Source Branching View for %s"
 msgstr "Näkymä lähdekoodin haarautumiseen %s:lle"
 
-#: browse.php:32
+#: browse.php:35
 #, php-format
 msgid "Source Directory of /%s"
 msgstr "Lähdehakemisto /%s"
 
-#: browse.php:126
-#, php-format
-msgid "Source Log for %s"
-msgstr "Lähdetiedoston lokitiedot %s:lle"
-
-#: lib/Chora.php:182
+#: lib/Chora.php:179
 msgid ""
 "SourceRoot not found! This could be a misconfiguration by the server "
 "administrator, or the server could be having temporary problems. Please try "
@@ -374,7 +375,7 @@ msgstr ""
 "palvelimen asetuksissa, tai palvelimella on väliaikainen virhetilanne. Yritä "
 "uudestaan myöhemmin."
 
-#: lib/Chora.php:470
+#: lib/Chora.php:513 lib/Chora.php:514
 msgid "Statistics"
 msgstr "Tilastot"
 
@@ -383,104 +384,79 @@ msgstr "Tilastot"
 msgid "Statistics for %s"
 msgstr "Tilastot %s:lle"
 
-#: templates/log/rev.inc:34
+#: templates/patchsets/ps.inc:22 templates/log/rev.inc:14
+msgid "Tags"
+msgstr "Tagit"
+
+#: templates/diff/hr/header.inc:38 templates/checkout/checkout.inc:6
 msgid "Tags:"
 msgstr "Tagit:"
 
-#: templates/log/request.inc:5
-msgid ""
-"This form allows you to request diffs between any two revisions of a file.  "
-"You may select a symbolic revision name using the selection box or you may "
-"type in a numeric name using the type-in text box."
-msgstr ""
-"Tällä lomakkeella voi pyytää vertailuja minkä tahansa kahden tiedoston "
-"revision välillä. Voit valita symbolisen revision nimen käyttäen "
-"valintalaatikkoa tai voit syöttää numeerisen nimen tekstikenttään."
-
-#: templates/headerbar.inc:16
+#: templates/headerbar.inc:9
 msgid "Tracking Branch"
 msgstr "Seurattava haara"
 
-#: templates/log/request.inc:23
-msgid "Type:"
-msgstr "Tyyppi:"
-
-#: templates/log/request.inc:27
+#: templates/diff/hr/header.inc:11
 msgid "Unified"
 msgstr "Yhdistetty"
 
-#: templates/log/request.inc:18 templates/log/request.inc:38
-msgid "Use Text Field"
-msgstr "Käytä tekstikenttää"
+#: templates/diff/hr/header.inc:24
+msgid "Unmodified"
+msgstr "Muokkaamaton"
 
 #: config/prefs.php.dist:37
 msgid "Use last viewed file or directory at login time"
 msgstr "Käytä viimeksi katsottua tiedostoa tai hakemistoa sisäänkirjautuessa"
 
-#: annotate.php:29 templates/log/rev.inc:9
+#: templates/diff/hr/header.inc:48 templates/diff/hr/header.inc:49
+#, php-format
+msgid "Version %s"
+msgstr "Versio %s"
+
+#: annotate.php:32 templates/log/header.inc:34
 msgid "View"
 msgstr "Näytä"
 
-#: templates/log/request.inc:60
-msgid "View Branch"
-msgstr "Näytä haara"
-
-#: templates/log/request.inc:54
-msgid "View revisions on:"
-msgstr "Näytä revisiot:"
-
-#: lib/Chora.php:472
+#: lib/Chora.php:517
 msgid "View:"
 msgstr "Näytä:"
 
-#: templates/directory/header.inc:18
+#: templates/directory/header.inc:12
 msgid "_Author"
 msgstr "_Tekijä"
 
-#: lib/Chora.php:465
+#: lib/Chora.php:504 lib/Chora.php:509
 msgid "_Branches"
 msgstr "_Haarat"
 
-#: lib/Chora.php:439
+#: lib/Chora.php:469
 msgid "_Browse"
 msgstr "_Selaa"
 
-#: templates/directory/header.inc:8
+#: templates/directory/header.inc:6
 msgid "_File"
 msgstr "_Tiedosto"
 
-#: lib/Chora.php:468
-msgid "_Graph"
-msgstr "_Graafi"
-
-#: lib/Chora.php:459
+#: lib/Chora.php:490
 msgid "_Logs"
 msgstr "_Lokitiedot"
 
-#: lib/Chora.php:462
+#: lib/Chora.php:497
 msgid "_Patchsets"
 msgstr "_Korjausjoukot"
 
-#: templates/directory/header.inc:13
+#: templates/directory/header.inc:9
 msgid "_Rev"
 msgstr "_Rev"
 
-#: lib/Chora.php:470
+#: lib/Chora.php:515
 msgid "_Statistics"
 msgstr "T_ilastot"
 
-#: templates/log/request.inc:35
-msgid "and:"
-msgstr "ja:"
-
-#: templates/log/rev.inc:5 templates/history/rev.inc:4
+#: templates/history/rev.inc:3
 #, php-format
 msgid "by %s"
-msgstr "%s:llä"
-
-#: templates/diff/hr/footer.inc:15
-msgid "changed lines"
-msgstr "muutettua riviä"
+msgstr "%s"
 
 #: lib/Chora.php:31
 msgid "day"
@@ -531,11 +507,6 @@ msgstr "sekunti"
 msgid "seconds"
 msgstr "sekuntia"
 
-#: templates/diff/hr/header.inc:13 templates/diff/hr/header.inc:14
-#, php-format
-msgid "version %s"
-msgstr "versio %s"
-
 #: lib/Chora.php:42
 msgid "very little time"
 msgstr "hyvin vähän aikaa"
diff --git a/po/fr_FR.po b/po/fr_FR.po
index 4c9646c..2563871 100644
--- a/po/fr_FR.po
+++ b/po/fr_FR.po
@@ -1,14 +1,14 @@
 # French translation for Chora
-# Copyright (C) 2001 Free Software Foundation, Inc.
+# Copyright 2001 Free Software Foundation, Inc.
 # Mathieu Arnold <mat at absolight.net>, 2001
-# Copyright (C) 2002 Thierry Thomas.
+# Copyright 2002, 2007 Thierry Thomas.
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: Chora 2.0-cvs\n"
+"Project-Id-Version: Chora 2.1-cvs\n"
 "Report-Msgid-Bugs-To: dev at lists.horde.org\n"
-"POT-Creation-Date: 2005-10-13 17:16+0200\n"
-"PO-Revision-Date: 2003-01-04 14:46+0100\n"
+"POT-Creation-Date: 2007-02-21 23:55+0100\n"
+"PO-Revision-Date: 2007-02-21 21:14+0100\n"
 "Last-Translator: Thierry Thomas <thierry at pompo.net>\n"
 "Language-Team: French <i18n at lists.horde.org>\n"
 "MIME-Version: 1.0\n"
@@ -20,43 +20,57 @@ msgstr ""
 msgid "#"
 msgstr "N°"
 
-#: templates/patchsets/ps.inc:12 templates/log/rev.inc:26
+#: templates/log/header.inc:16
+#, php-format
+msgid ""
+"%1$s shows diffs to the previous revision. If you select a revision by "
+"clicking its row, %1$s will show the differences to the selected row."
+msgstr ""
+"%1$s montre les différences avec la version précédente. If vous sélectionnez "
+"une révision en cliquant sur sa ligne, %1$s montrera le delta avec la "
+"version sélectionnée."
+
+#: co.php:82
+#, php-format
+msgid "%s Revision %s (%s ago)"
+msgstr "%s Révision %s (il y a %s)"
+
+#: templates/annotate/footer.inc:22
 #, php-format
 msgid "%s ago"
 msgstr "il y a %s"
 
-#: templates/diff/hr/footer.inc:19
-#, php-format
-msgid "Added in v.%s"
-msgstr "Ajouté en v.%s"
+#: templates/diff/hr/header.inc:24
+msgid "Added"
+msgstr "Ajouté"
 
-#: templates/log/request.inc:57
+#: templates/log/header.inc:28
 msgid "All Branches"
 msgstr "Toutes les branches"
 
-#: co.php:72 templates/log/rev.inc:8
+#: co.php:87
 msgid "Annotate"
 msgstr "Annoter"
 
-#: templates/directory/header.inc:18 templates/stats/stats.inc:3
+#: templates/stats/stats.inc:4 templates/patchsets/header.inc:15
+#: templates/log/header.inc:57 templates/directory/header.inc:12
 #: templates/annotate/header.inc:5
 msgid "Author"
 msgstr "Auteur"
 
+#: templates/checkout/checkout.inc:4
+msgid "Author:"
+msgstr "Auteur :"
+
 #: templates/directory/back.inc:4
 msgid "Back"
 msgstr "Retour"
 
-#: templates/checkout/checkout.inc:27 templates/log/rev.inc:31
-msgid "Branch Point for:"
-msgstr "Embranchement pour :"
-
-#: templates/checkout/checkout.inc:11 templates/patchsets/ps.inc:14
-#: templates/log/rev.inc:28
+#: templates/checkout/checkout.inc:5
 msgid "Branch:"
 msgstr "Branche :"
 
-#: lib/Chora.php:465
+#: lib/Chora.php:449
 msgid "Branches"
 msgstr "Branches"
 
@@ -64,50 +78,36 @@ msgstr "Branches"
 msgid "Branching to"
 msgstr "Embranchement pour"
 
-#: templates/checkout/checkout.inc:35
-#, php-format
-msgid "Changed since <b>%s</b>"
-msgstr "Modifications depuis <b>%s</b> "
-
-#: templates/log/rev.inc:37
-#, fuzzy, php-format
-msgid "Changed since <strong>%s</strong>"
-msgstr "Modifications depuis <b>%s</b> "
-
-#: templates/history/rev.inc:8
+#: templates/history/rev.inc:7
 #, php-format
 msgid "Changed: %s"
 msgstr "Modifications : %s"
 
-#: co.php:70
-#, fuzzy, php-format
-msgid "Checkout of %s (revision %s)"
-msgstr "Checkout de %s (révision %s)"
+#: templates/checkout/checkout.inc:12
+msgid "Checkout"
+msgstr "Récupérer"
 
-#: templates/cvsgraph/cvsgraph.inc:11
+#: templates/cvsgraph/cvsgraph.inc:5
 msgid ""
 "Click on the links in between revisions to get a diff between those "
 "revisions."
 msgstr ""
+"Cliquer sur les liens entre les révisions pour obtenir le delta entre ces "
+"révisions."
 
-#: templates/cvsgraph/cvsgraph.inc:10
+#: templates/cvsgraph/cvsgraph.inc:4
 msgid "Click on the revisions and branches to display the file."
-msgstr ""
-
-#: templates/log/rev.inc:42 templates/log/rev.inc:50
-msgid "Colored"
-msgstr ""
+msgstr "Cliquer sur les révisions et les branches pour afficher le fichier."
 
-#: templates/checkout/checkout.inc:19 templates/patchsets/ps.inc:17
-#, fuzzy
-msgid "Commit Tags:"
-msgstr "Tags :"
+#: templates/diff/hr/header.inc:12
+msgid "Column"
+msgstr "Colonne"
 
-#: templates/stats/stats.inc:4
+#: templates/stats/stats.inc:5
 msgid "Commits"
-msgstr ""
+msgstr "Validations"
 
-#: templates/log/request.inc:28
+#: templates/diff/hr/header.inc:11
 msgid "Context"
 msgstr "Contexte"
 
@@ -115,62 +115,50 @@ msgstr "Contexte"
 msgid "Customize tasks to run upon logging in to Chora."
 msgstr "Paramétrer les tâches à exécuter lors de la connexion à Chora."
 
-#: templates/directory/header.inc:23
-#, fuzzy
+#: templates/directory/header.inc:15
 msgid "Da_te"
-msgstr "Date"
+msgstr "Da_te"
 
-#: templates/directory/header.inc:23
+#: templates/patchsets/header.inc:14 templates/log/header.inc:56
+#: templates/directory/header.inc:15
 msgid "Date"
 msgstr "Date"
 
-#: patchsets.php:66
-#, fuzzy
+#: patchsets.php:83
 msgid "Deleted"
-msgstr "Fichier supprimé"
+msgstr "Supprimé"
 
 #: templates/directory/file.inc:5 templates/directory/file.inc:17
 msgid "Deleted File"
 msgstr "Fichier supprimé"
 
-#: templates/log/rev.inc:13
-#, fuzzy
-msgid "Deselect"
-msgstr "Dé-sélectionner"
-
-#: patchsets.php:63
-#, fuzzy
+#: patchsets.php:80 templates/log/header.inc:15
 msgid "Diff"
-msgstr "Récupérer le delta"
+msgstr "Delta"
 
-#: patchsets.php:46
-msgid "Diff All Files"
-msgstr ""
-
-#: diff.php:69
+#: diff.php:51
 #, php-format
 msgid "Diff for %s between version %s and %s"
 msgstr "Delta de %s entre les versions %s et %s"
 
-#: templates/log/rev.inc:49
-#, php-format
-msgid "Diffs to <strong>%s</strong>"
-msgstr ""
-
-#: templates/log/rev.inc:41
-#, php-format
-msgid "Diffs to version %s"
-msgstr "Delta avec la version %s"
-
-#: templates/directory/dir.inc:5 templates/directory/dir.inc:7
+#: templates/directory/dir.inc:4 templates/directory/dir.inc:6
 msgid "Directory"
 msgstr "Répertoire"
 
-#: annotate.php:30 co.php:73 templates/log/rev.inc:10
+#: annotate.php:31 co.php:88
 msgid "Download"
 msgstr "Télécharger"
 
-#: templates/log/request.inc:30
+#: templates/diff/hr/header.inc:8
+msgid "Download diff as: "
+msgstr "Télécharger le delta en tant que : "
+
+#: templates/checkout/checkout.inc:23
+#, php-format
+msgid "Download revision %s"
+msgstr "Télécharger la révision %s"
+
+#: templates/diff/hr/header.inc:13
 msgid "Ed Script"
 msgstr "Script Ed"
 
@@ -183,108 +171,99 @@ msgid "Error Encountered"
 msgstr "Une erreur est survenue"
 
 #: templates/directory/file.inc:7 templates/directory/file.inc:19
-#: templates/directory/header.inc:8
+#: templates/directory/header.inc:6
 msgid "File"
 msgstr "Fichier"
 
-#: templates/patchsets/ps.inc:21
-#, fuzzy
-msgid "Files Changed:"
-msgstr "Modifications : %s"
+#: templates/patchsets/header.inc:16
+msgid "Files"
+msgstr "Fichiers"
 
-#: templates/log/request.inc:44
-msgid "Get Diffs"
+#: templates/diff/hr/header.inc:15
+msgid "Get Diff"
 msgstr "Récupérer le delta"
 
-#: lib/Chora.php:468
+#: lib/Chora.php:452
 msgid "Graph"
-msgstr ""
+msgstr "Graphe"
 
 #: cvsgraph.php:48
 #, php-format
 msgid "Graph for %s"
-msgstr ""
+msgstr "Graphe de %s"
 
-#: browse.php:40
+#: browse.php:55
 msgid "Hide Deleted Files"
 msgstr "Cacher les fichiers supprimés"
 
-#: browse.php:40
-#, fuzzy
+#: browse.php:55
 msgid "Hide _Deleted Files"
-msgstr "Cacher les fichiers supprimés"
-
-#: templates/log/request.inc:26
-msgid "Human Readable"
-msgstr "Embellie"
+msgstr "Cacher les fichiers _supprimés"
 
-#: templates/directory/header.inc:25
+#: templates/directory/header.inc:17
 msgid "Last Log"
-msgstr "Dernier message du journal"
+msgstr "Dernier journal"
 
-#: templates/diff/hr/header.inc:5
-#, php-format
-msgid "Last Log Message for rev %s:"
-msgstr "Dernier message du journal pour la révision %s :"
-
-#: templates/diff/hr/footer.inc:5
-msgid "Legend:"
-msgstr ""
-
-#: templates/annotate/header.inc:7
+#: templates/annotate/header.inc:8
 msgid "Line"
 msgstr "Ligne"
 
-#: templates/diff/hr/row.inc:3 templates/diff/hr/row.inc:7
+#: templates/diff/hr/row.inc:3 templates/diff/hr/row.inc:6
 #, php-format
 msgid "Line %s"
 msgstr "Ligne %s"
 
-#: templates/headerbar.inc:11
+#: templates/headerbar.inc:5
 msgid "Location:"
 msgstr "Emplacement :"
 
-#: templates/checkout/checkout.inc:42
-#, fuzzy
-msgid "Log:"
-msgstr "Journaux"
+#: templates/patchsets/header.inc:17 templates/log/header.inc:58
+#: templates/checkout/checkout.inc:1
+msgid "Log Message"
+msgstr "Message du journal"
 
 #: config/prefs.php.dist:10
 msgid "Login Tasks"
 msgstr "Tâches à la connexion"
 
-#: lib/Chora.php:459
+#: lib/Chora.php:443
 msgid "Logs"
 msgstr "Journaux"
 
-#: templates/log/rev.inc:43 templates/log/rev.inc:51
-#, fuzzy
-msgid "Long"
-msgstr "Journaux"
-
-#: diff.php:61
+#: diff.php:25
 msgid "Malformed Query"
-msgstr ""
+msgstr "Requête mal formée"
 
-#: patchsets.php:59
-#, fuzzy
+#: lib/Block/tree_menu.php:3
+msgid "Menu List"
+msgstr "Menu"
+
+#: templates/diff/hr/header.inc:25
+msgid "Modified"
+msgstr "Modifié"
+
+#: patchsets.php:76
 msgid "New File"
-msgstr "Fichier"
+msgstr "Nouveau fichier"
 
-#: templates/diff/hr/nochange.inc:6
-msgid "No Viewable Change"
+#: templates/diff/hr/nochange.inc:3
+msgid "No Visible Changes"
 msgstr "Aucun changement visible"
 
-#: templates/log/rev.inc:44 templates/log/rev.inc:52
-#, fuzzy
-msgid "NoWhitespaceChanges"
-msgstr "Aucun changement visible"
+#: templates/log/header.inc:44
+#, php-format
+msgid ""
+"Only showing the 100 latest revisions. %sShow all revisions?</a> (may take a "
+"while)."
+msgstr ""
+"Seules les 100 dernières révisions sont présentées. %sMontrer toutes les "
+"révisions ?</a> (peu prendre un certain temps)."
 
 #: config/prefs.php.dist:9
 msgid "Other Options"
 msgstr "Autres options"
 
-#: lib/Chora.php:368
+#: lib/Chora.php:346
 msgid "Other Repositories"
 msgstr "Autres dépôts"
 
@@ -292,213 +271,196 @@ msgstr "Autres d
 msgid "Parent Directory"
 msgstr "Répertoire parent"
 
-#: templates/patchsets/ps.inc:6
-#, php-format
-msgid "PatchSet %s</span> by %s"
-msgstr ""
+#: templates/patchsets/header.inc:13
+msgid "Patchset"
+msgstr "Jeu de patches"
 
-#: lib/Chora.php:462
+#: lib/Chora.php:446
 msgid "Patchsets"
-msgstr ""
+msgstr "Jeux de patches"
 
-#: patchsets.php:27
-#, fuzzy, php-format
+#: patchsets.php:32
+#, php-format
 msgid "Patchsets for %s"
-msgstr "Statistiques CVS de %s"
+msgstr "Jeux de patches pour %s"
 
 #: templates/error_page.inc:13
 msgid "Please contact"
-msgstr "Veuillez contactez"
+msgstr "Veuillez contacter"
 
-#: templates/diff/hr/footer.inc:11
-#, php-format
-msgid "Removed in v.%s"
-msgstr "Supprimé en v.%s"
+#: templates/annotate/header.inc:7
+msgid "Prev"
+msgstr "Préc"
+
+#: templates/diff/hr/header.inc:26
+msgid "Removed"
+msgstr "Retiré"
 
-#: templates/log/request.inc:14
-msgid "Retrieve diffs between:"
-msgstr "Récupérer les deltas entre :"
+#: lib/api.php:30
+msgid "Repositories"
+msgstr "Dépôts"
 
-#: templates/directory/header.inc:13 templates/annotate/header.inc:6
+#: templates/directory/header.inc:9 templates/annotate/header.inc:6
 msgid "Rev"
 msgstr "Rév"
 
-#: co.php:77
-#, php-format
-msgid "Revision %s for file %s not found."
-msgstr ""
+#: templates/log/header.inc:55
+msgid "Revision"
+msgstr "Révision"
 
-#: templates/checkout/checkout.inc:4
+#: browse.php:150
 #, php-format
-msgid "Revision <b>%s</b>, <i>%s</i> (%s ago) by %s"
-msgstr ""
+msgid "Revisions for %s"
+msgstr "Révision pour %s"
 
-#: templates/log/rev.inc:15
-msgid "Select for Diff"
-msgstr "Sélect. pour delta"
+#: templates/patchsets/header.inc:4
+msgid "Search Patchsets:"
+msgstr "Rechercher des jeux de patches :"
 
-#: browse.php:42
+#: templates/log/header.inc:4
+msgid "Search Revisions:"
+msgstr "Rechercher des révisions :"
+
+#: templates/log/header.inc:26
+msgid "Show Branch:"
+msgstr "Montrer la branche :"
+
+#: browse.php:57
 msgid "Show Deleted Files"
 msgstr "Afficher les fichiers supprimés"
 
-#: browse.php:42
-#, fuzzy
+#: templates/log/header.inc:17
+msgid "Show Differences"
+msgstr "Montrer les différences"
+
+#: browse.php:57
 msgid "Show _Deleted Files"
-msgstr "Afficher les fichiers supprimés"
+msgstr "Afficher les fichiers _supprimés"
+
+#: templates/log/rev.inc:4
+msgid "Show changes to the previous revision"
+msgstr "Montrer les changements par rapport à la révision précédente"
 
-#: templates/log/request.inc:29
-msgid "Side-by-Side"
-msgstr "Côte à côte"
+#: templates/log/rev.inc:6
+msgid "Show changes to the selected revision"
+msgstr "Montrer les changements par rapport à la révision sélectionnée"
 
-#: templates/directory/header.inc:7 templates/directory/header.inc:12
-#: templates/directory/header.inc:17 templates/directory/header.inc:22
-msgid "Sort Order"
-msgstr "Ordre de tri"
+#: templates/log/header.inc:12
+#, php-format
+msgid "Show diffs between %s and %s"
+msgstr "Montrer les deltas entre %s et %s"
 
-#: annotate.php:27
-#, fuzzy, php-format
-msgid "Source Annotation of %s for version %s"
-msgstr "Annotations de %s pour la version %s"
+#: annotate.php:28
+#, php-format
+msgid "Source Annotation of %s (revision %s)"
+msgstr "Annotation de %s (révision %s)"
 
-#: history.php:160
-#, fuzzy, php-format
+#: history.php:161
+#, php-format
 msgid "Source Branching View for %s"
 msgstr "Vue par branches pour %s"
 
-#: browse.php:32
-#, fuzzy, php-format
+#: browse.php:49
+#, php-format
 msgid "Source Directory of /%s"
-msgstr "Répertoire /%s"
+msgstr "Répertoire source de /%s"
 
-#: browse.php:126
-#, fuzzy, php-format
-msgid "Source Log for %s"
-msgstr "Journal CVS de %s"
-
-#: lib/Chora.php:182
+#: lib/Chora.php:176
 msgid ""
 "SourceRoot not found! This could be a misconfiguration by the server "
 "administrator, or the server could be having temporary problems. Please try "
 "again later."
 msgstr ""
+"Racine non trouvée ! Ça peut être dû à une mauvaise configuration du "
+"serveur, ou à un problème temporaire. Veuillez réessayer plus tard."
 
-#: lib/Chora.php:470
+#: lib/Chora.php:454
 msgid "Statistics"
 msgstr "Statistiques"
 
-#: stats.php:27
-#, fuzzy, php-format
+#: stats.php:25
+#, php-format
 msgid "Statistics for %s"
-msgstr "Statistiques CVS de %s"
+msgstr "Statistiques de %s"
+
+#: templates/patchsets/ps.inc:22 templates/log/rev.inc:14
+msgid "Tags"
+msgstr "Tags"
 
-#: templates/log/rev.inc:34
-#, fuzzy
+#: templates/checkout/checkout.inc:6
 msgid "Tags:"
 msgstr "Tags :"
 
-#: templates/log/request.inc:5
-#, fuzzy
-msgid ""
-"This form allows you to request diffs between any two revisions of a file.  "
-"You may select a symbolic revision name using the selection box or you may "
-"type in a numeric name using the type-in text box."
-msgstr ""
-" Ce formulaire vous permet de demander des deltas entre n'importe quelle "
-"révision d'un fichier. Vous pouvez sélectionner un nom de révision "
-"symbolique en utilisant le menu de sélection ou vous pouvez inscrire un "
-"numéro de révision dans le champ texte."
-
-#: templates/headerbar.inc:16
+#: templates/headerbar.inc:9
 msgid "Tracking Branch"
 msgstr "Branche suivie"
 
-#: templates/log/request.inc:23
-msgid "Type:"
-msgstr "Type :"
-
-#: templates/log/request.inc:27
+#: templates/diff/hr/header.inc:10
 msgid "Unified"
 msgstr "Unifié"
 
-#: templates/log/request.inc:18 templates/log/request.inc:38
-msgid "Use Text Field"
-msgstr "Utiliser le champ texte"
+#: templates/diff/hr/header.inc:23
+msgid "Unmodified"
+msgstr "Inchangé"
 
 #: config/prefs.php.dist:37
 msgid "Use last viewed file or directory at login time"
 msgstr "Revenir au dernier endroit visité lors de la connexion"
 
-#: annotate.php:29 templates/log/rev.inc:9
+#: templates/diff/hr/header.inc:30 templates/diff/hr/header.inc:31
+#, php-format
+msgid "Version %s"
+msgstr "Version %s"
+
+#: annotate.php:30 templates/log/header.inc:31
 msgid "View"
 msgstr "Voir"
 
-#: templates/log/request.inc:60
-msgid "View Branch"
-msgstr "Voir les branches"
-
-#: templates/log/request.inc:54
-msgid "View revisions on:"
-msgstr "Voir les révisions pour :"
-
-#: lib/Chora.php:472
+#: lib/Chora.php:456
 msgid "View:"
 msgstr "Voir :"
 
-#: templates/directory/header.inc:18
-#, fuzzy
+#: templates/directory/header.inc:12
 msgid "_Author"
-msgstr "Auteur"
+msgstr "_Auteur"
 
-#: lib/Chora.php:465
-#, fuzzy
+#: lib/Chora.php:449
 msgid "_Branches"
-msgstr "Branches"
+msgstr "_Branches"
 
-#: lib/Chora.php:439
+#: lib/Chora.php:425
 msgid "_Browse"
-msgstr ""
+msgstr "_Lister"
 
-#: templates/directory/header.inc:8
-#, fuzzy
+#: templates/directory/header.inc:6
 msgid "_File"
-msgstr "Fichier"
+msgstr "_Fichier"
 
-#: lib/Chora.php:468
+#: lib/Chora.php:452
 msgid "_Graph"
-msgstr ""
+msgstr "_Graphe"
 
-#: lib/Chora.php:459
-#, fuzzy
+#: lib/Chora.php:443
 msgid "_Logs"
-msgstr "Journaux"
+msgstr "_Journaux"
 
-#: lib/Chora.php:462
+#: lib/Chora.php:446
 msgid "_Patchsets"
-msgstr ""
+msgstr "Jeux de _patches"
 
-#: templates/directory/header.inc:13
-#, fuzzy
+#: templates/directory/header.inc:9
 msgid "_Rev"
-msgstr "Rév"
+msgstr "_Rév"
 
-#: lib/Chora.php:470
-#, fuzzy
+#: lib/Chora.php:454
 msgid "_Statistics"
-msgstr "Statistiques"
-
-#: templates/log/request.inc:35
-msgid "and:"
-msgstr "et :"
+msgstr "_Statistiques"
 
-#: templates/history/rev.inc:4 templates/log/rev.inc:5
+#: templates/history/rev.inc:3
 #, php-format
 msgid "by %s"
 msgstr "par %s"
 
-#: templates/diff/hr/footer.inc:15
-msgid "changed lines"
-msgstr "lignes modifiées"
-
 #: lib/Chora.php:31
 msgid "day"
 msgstr "jour"
@@ -548,11 +510,6 @@ msgstr "seconde"
 msgid "seconds"
 msgstr "secondes"
 
-#: templates/diff/hr/header.inc:13 templates/diff/hr/header.inc:14
-#, php-format
-msgid "version %s"
-msgstr "version %s"
-
 #: lib/Chora.php:42
 msgid "very little time"
 msgstr "très peu de temps"
diff --git a/po/it_IT.po b/po/it_IT.po
new file mode 100644
index 0000000..5e072bd
--- /dev/null
+++ b/po/it_IT.po
@@ -0,0 +1,528 @@
+# Italian translations for chora package.
+# Copyright 2008-2009 The Horde Project
+# This file is distributed under the same license as the chora package.
+# Fabio Pedretti <fabio.pedretti at ing.unibs.it>, 2008.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: chora v2.1-cvs\n"
+"Report-Msgid-Bugs-To: dev at lists.horde.org\n"
+"POT-Creation-Date: 2008-09-05 18:12+0200\n"
+"PO-Revision-Date: 2008-09-05 19:15+0200\n"
+"Last-Translator: Fabio Pedretti <fabio.pedretti at ing.unibs.it>\n"
+"Language-Team: i18n at lists.horde.org\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-1\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: templates/annotate/header.inc:4
+msgid "#"
+msgstr "#"
+
+#: templates/log/header.inc:17
+#, php-format
+msgid ""
+"%1$s shows diffs to the previous revision. If you select a revision by "
+"clicking its row, %1$s will show the differences to the selected row."
+msgstr ""
+"%1$s mostra le differenze con la revisione precedente. Se scegi una "
+"revisione cliccando la sua riga, %1$s ti mostrerà le differenze con la riga "
+"selezionata."
+
+#: co.php:84
+#, php-format
+msgid "%s Revision %s (%s ago)"
+msgstr "%s revisione %s (%s prima)"
+
+#: templates/annotate/footer.inc:22
+#, php-format
+msgid "%s ago"
+msgstr "%s prima"
+
+#: templates/diff/hr/header.inc:25
+msgid "Added"
+msgstr "Aggiunto"
+
+#: templates/log/header.inc:31
+msgid "All Branches"
+msgstr "Tutti i rami"
+
+#: co.php:89
+msgid "Annotate"
+msgstr "Annotato"
+
+#: templates/stats/stats.inc:4 templates/patchsets/header.inc:15
+#: templates/log/header.inc:49 templates/directory/header.inc:12
+#: templates/annotate/header.inc:5
+msgid "Author"
+msgstr "Autore"
+
+#: templates/diff/hr/header.inc:35 templates/checkout/checkout.inc:4
+msgid "Author:"
+msgstr "Autore:"
+
+#: templates/directory/back.inc:4
+msgid "Back"
+msgstr "Indietro"
+
+#: templates/diff/hr/header.inc:37 templates/checkout/checkout.inc:5
+msgid "Branch:"
+msgstr "Ramo:"
+
+#: lib/Chora.php:498 lib/Chora.php:499 lib/Chora.php:503 lib/Chora.php:504
+msgid "Branches"
+msgstr "Rami"
+
+#: templates/history/branch_cell.inc:2
+msgid "Branching to"
+msgstr "Ramificato da"
+
+#: lib/Chora.php:388
+msgid "Change repositories:"
+msgstr "Cambia deposito:"
+
+#: templates/history/rev.inc:7
+#, php-format
+msgid "Changed: %s"
+msgstr "Cambiato: %s"
+
+#: templates/checkout/checkout.inc:12
+msgid "Checkout"
+msgstr "Controllo"
+
+#: templates/cvsgraph/cvsgraph.inc:5
+msgid ""
+"Click on the links in between revisions to get a diff between those "
+"revisions."
+msgstr ""
+"Clicca sul collegamento tra le revisioni per ottenere una la differenza tra "
+"loro."
+
+#: templates/cvsgraph/cvsgraph.inc:4
+msgid "Click on the revisions and branches to display the file."
+msgstr "Clicca sulle revisioni e ramifica per vedere il file."
+
+#: templates/diff/hr/header.inc:13
+msgid "Column"
+msgstr "Colonna"
+
+#: templates/stats/stats.inc:5
+msgid "Commits"
+msgstr "Assegnare"
+
+#: templates/diff/hr/header.inc:12
+msgid "Context"
+msgstr "Contesto"
+
+#: config/prefs.php.dist:11
+msgid "Customize tasks to run upon logging in to Chora."
+msgstr "Personalizza le operazioni da eseguire quando entri in Chora."
+
+#: templates/directory/header.inc:15
+msgid "Da_te"
+msgstr "Da_ta"
+
+#: templates/patchsets/header.inc:14 templates/log/header.inc:48
+#: templates/directory/header.inc:15
+msgid "Date"
+msgstr "Data"
+
+#: templates/diff/hr/header.inc:36
+msgid "Date:"
+msgstr "Data:"
+
+#: patchsets.php:79
+msgid "Deleted"
+msgstr "Eliminato"
+
+#: templates/directory/file.inc:5 templates/directory/file.inc:28
+msgid "Deleted File"
+msgstr "File eliminato"
+
+#: patchsets.php:76 templates/log/header.inc:16
+msgid "Diff"
+msgstr "Differenza"
+
+#: diff.php:71
+#, php-format
+msgid "Diff for %s between version %s and %s"
+msgstr "Differenza per %s tra le versioni %s e %s"
+
+#: templates/directory/dir.inc:4 templates/directory/dir.inc:6
+msgid "Directory"
+msgstr "Directory"
+
+#: annotate.php:33 co.php:90
+msgid "Download"
+msgstr "Scarica"
+
+#: templates/diff/hr/header.inc:9
+msgid "Download diff as: "
+msgstr "Scarica differenza come: "
+
+#: templates/checkout/checkout.inc:23
+#, php-format
+msgid "Download revision %s"
+msgstr "Scarica revisione %s"
+
+#: templates/diff/hr/header.inc:14
+msgid "Ed Script"
+msgstr "Ed Script"
+
+#: templates/error_page.inc:4
+msgid "Error"
+msgstr "Errore"
+
+#: templates/error_page.inc:7
+msgid "Error Encountered"
+msgstr "Si è verificato un errore"
+
+#: templates/directory/file.inc:7 templates/directory/file.inc:30
+#: templates/directory/header.inc:6
+msgid "File"
+msgstr "File"
+
+#: templates/patchsets/header.inc:16
+msgid "Files"
+msgstr "File"
+
+#: templates/diff/hr/header.inc:16
+msgid "Get Diff"
+msgstr "Ottieni differenze"
+
+#: cvsgraph.php:51
+#, php-format
+msgid "Graph for %s"
+msgstr "Grafico di %s"
+
+#: browse.php:42
+msgid "Hide Deleted Files"
+msgstr "Nascondi File eliminati"
+
+#: browse.php:44
+msgid "Hide _Deleted Files"
+msgstr "_Nascondi File Eliminati"
+
+#: templates/directory/header.inc:17
+msgid "Last Log"
+msgstr "Ultima autenticazione"
+
+#: templates/annotate/header.inc:8
+msgid "Line"
+msgstr "Riga"
+
+#: templates/diff/hr/row.inc:3 templates/diff/hr/row.inc:6
+#, php-format
+msgid "Line %s"
+msgstr "Riga %s"
+
+#: templates/headerbar.inc:5
+msgid "Location:"
+msgstr "Luogo:"
+
+#: templates/patchsets/header.inc:17 templates/log/header.inc:50
+#: templates/diff/hr/header.inc:30 templates/checkout/checkout.inc:1
+msgid "Log Message"
+msgstr "Messaggio utente"
+
+#: config/prefs.php.dist:10
+msgid "Login Tasks"
+msgstr "Operazioni di autenticazione"
+
+#: lib/Chora.php:484 lib/Chora.php:485
+msgid "Logs"
+msgstr "Utenti"
+
+#: diff.php:44
+msgid "Malformed Query"
+msgstr "Richiesta malformulata"
+
+#: lib/Block/tree_menu.php:3
+msgid "Menu List"
+msgstr "Menu Lista"
+
+#: templates/diff/hr/header.inc:26
+msgid "Modified"
+msgstr "Modificato"
+
+#: patchsets.php:72
+msgid "New File"
+msgstr "Nuovo File"
+
+#: templates/diff/hr/nochange.inc:3
+msgid "No Visible Changes"
+msgstr "Nessun cambiamento visibile"
+
+#: templates/log/footer.inc:8
+#, php-format
+msgid ""
+"Only showing the 100 latest revisions. %sShow all revisions?</a> (may take a "
+"while)."
+msgstr ""
+"Mostra solo le 100 ultime revisioni. %smostra tutte le revisioni?</a> "
+"(potrebbe prendere  un while)."
+
+#: config/prefs.php.dist:9
+msgid "Other Options"
+msgstr "Altre Opzioni"
+
+#: templates/directory/back.inc:4
+msgid "Parent Directory"
+msgstr "Directory Principale"
+
+#: templates/patchsets/header.inc:13
+msgid "Patchset"
+msgstr "Patchset"
+
+#: lib/Chora.php:491 lib/Chora.php:492
+msgid "Patchsets"
+msgstr "Patchset"
+
+#: patchsets.php:35
+#, php-format
+msgid "Patchsets for %s"
+msgstr "Patchset per %s"
+
+#: templates/error_page.inc:13
+msgid "Please contact"
+msgstr "Contatta"
+
+#: templates/annotate/header.inc:7
+msgid "Prev"
+msgstr "Precedente"
+
+#: templates/diff/hr/header.inc:27
+msgid "Removed"
+msgstr "Eliminato"
+
+#: lib/api.php:30
+msgid "Repositories"
+msgstr "Deposita"
+
+#: templates/directory/header.inc:9 templates/annotate/header.inc:6
+msgid "Rev"
+msgstr "Rivedi"
+
+#: templates/log/header.inc:47
+msgid "Revision"
+msgstr "Revisione"
+
+#: browse.php:157
+#, php-format
+msgid "Revisions for %s"
+msgstr "Revisioni per %s"
+
+#: templates/patchsets/header.inc:4
+msgid "Search Patchsets:"
+msgstr "Ricerca Patchset:"
+
+#: templates/log/header.inc:4
+msgid "Search Revisions:"
+msgstr "Ricerca revisioni:"
+
+#: templates/log/header.inc:29
+msgid "Show Branch:"
+msgstr "Mostra ramo:"
+
+#: browse.php:42
+msgid "Show Deleted Files"
+msgstr "Mostra File Eliminati"
+
+#: templates/log/header.inc:18
+msgid "Show Differences"
+msgstr "Mostra differenze"
+
+#: browse.php:44
+msgid "Show _Deleted Files"
+msgstr "_Mostra File Eliminati"
+
+#: templates/log/rev.inc:4
+msgid "Show changes to the previous revision"
+msgstr "Mostra cambiamenti alla revisione precedente"
+
+#: templates/log/rev.inc:6
+msgid "Show changes to the selected revision"
+msgstr "Mostra cambiamenti alla revisione selezionata"
+
+#: templates/log/header.inc:13
+#, php-format
+msgid "Show diffs between %s and %s"
+msgstr "Mostra differenze tra %s e %s"
+
+#: annotate.php:30
+#, php-format
+msgid "Source Annotation of %s (revision %s)"
+msgstr "Sorgente annotazione di %s (revisione %s)"
+
+#: history.php:163
+#, php-format
+msgid "Source Branching View for %s"
+msgstr "Ramo sorgente visto per %s"
+
+#: browse.php:35
+#, php-format
+msgid "Source Directory of /%s"
+msgstr "Directory sorgente di /%s"
+
+#: lib/Chora.php:179
+msgid ""
+"SourceRoot not found! This could be a misconfiguration by the server "
+"administrator, or the server could be having temporary problems. Please try "
+"again later."
+msgstr ""
+"Sorgente radice non trovata! Questo potrebbe essere causato da un errata "
+"configurazione dell'amministratore del server, oppure il server potrebbe "
+"avere dei problemi temporanei. Riprovare più tardi."
+
+#: lib/Chora.php:509 lib/Chora.php:510
+msgid "Statistics"
+msgstr "Statistiche"
+
+#: stats.php:27
+#, php-format
+msgid "Statistics for %s"
+msgstr "Statistiche per %s"
+
+#: templates/patchsets/ps.inc:22 templates/log/rev.inc:14
+msgid "Tags"
+msgstr "Tag"
+
+#: templates/diff/hr/header.inc:38 templates/checkout/checkout.inc:6
+msgid "Tags:"
+msgstr "Tag:"
+
+#: templates/headerbar.inc:9
+msgid "Tracking Branch"
+msgstr "Traccia ramificazioni"
+
+#: templates/diff/hr/header.inc:11
+msgid "Unified"
+msgstr "Unificato"
+
+#: templates/diff/hr/header.inc:24
+msgid "Unmodified"
+msgstr "Non modificato"
+
+#: config/prefs.php.dist:37
+msgid "Use last viewed file or directory at login time"
+msgstr "Usa l'ultimo file visto o la directory che si aveva all'autenticazione"
+
+#: templates/diff/hr/header.inc:48 templates/diff/hr/header.inc:49
+#, php-format
+msgid "Version %s"
+msgstr "Versione %s"
+
+#: annotate.php:32 templates/log/header.inc:34
+msgid "View"
+msgstr "Visualizza"
+
+#: lib/Chora.php:513
+msgid "View:"
+msgstr "Visualizza:"
+
+#: templates/directory/header.inc:12
+msgid "_Author"
+msgstr "_Autore"
+
+#: lib/Chora.php:500 lib/Chora.php:505
+msgid "_Branches"
+msgstr "_Ramificazioni"
+
+#: lib/Chora.php:465
+msgid "_Browse"
+msgstr "_Mostra"
+
+#: templates/directory/header.inc:6
+msgid "_File"
+msgstr "_File"
+
+#: lib/Chora.php:486
+msgid "_Logs"
+msgstr "_Logs"
+
+#: lib/Chora.php:493
+msgid "_Patchsets"
+msgstr "_Patchsets"
+
+#: templates/directory/header.inc:9
+msgid "_Rev"
+msgstr "_Rivedi"
+
+#: lib/Chora.php:511
+msgid "_Statistics"
+msgstr "_Statistiche"
+
+#: templates/history/rev.inc:3
+#, php-format
+msgid "by %s"
+msgstr "da %s"
+
+#: lib/Chora.php:31
+msgid "day"
+msgstr "giorno"
+
+#: lib/Chora.php:31
+msgid "days"
+msgstr "giorni"
+
+#: templates/error_page.inc:14
+msgid "for further information."
+msgstr "per ulteriori informazioni"
+
+#: lib/Chora.php:30
+msgid "hour"
+msgstr "ora"
+
+#: lib/Chora.php:30
+msgid "hours"
+msgstr "ore"
+
+#: lib/Chora.php:29
+msgid "minute"
+msgstr "minuto"
+
+#: lib/Chora.php:29
+msgid "minutes"
+msgstr "minuti"
+
+#: lib/Chora.php:33
+msgid "month"
+msgstr "mese"
+
+#: lib/Chora.php:33
+msgid "months"
+msgstr "mesi"
+
+#: templates/history/branch_cell.inc:6
+#, php-format
+msgid "revision %s"
+msgstr "revisione %s"
+
+#: lib/Chora.php:28
+msgid "second"
+msgstr "secondo"
+
+#: lib/Chora.php:28
+msgid "seconds"
+msgstr "secondi"
+
+#: lib/Chora.php:42
+msgid "very little time"
+msgstr "poco tempo"
+
+#: lib/Chora.php:32
+msgid "week"
+msgstr "settimana"
+
+#: lib/Chora.php:32
+msgid "weeks"
+msgstr "settimane"
+
+#: lib/Chora.php:34
+msgid "year"
+msgstr "anno"
+
+#: lib/Chora.php:34
+msgid "years"
+msgstr "anni"
diff --git a/po/nb_NO.po b/po/nb_NO.po
index 664bf66..1179e1d 100644
--- a/po/nb_NO.po
+++ b/po/nb_NO.po
@@ -1,12 +1,11 @@
 # Norwegian Chora Translation.
-# Copyright (C) 2001 Oystein Steimler.
+# Copyright 2001 Oystein Steimler.
 # Oystein Steimler <oystein at rexta.net>, 2001.
 #
 msgid ""
 msgstr ""
 "Project-Id-Version: Chora 2.0-cvs\n"
-"Report-Msgid-Bugs-To: dev at lists.horde.org\n"
-"POT-Creation-Date: 2005-10-13 17:16+0200\n"
+"POT-Creation-Date: 2001-12-28 19:16+0100\n"
 "PO-Revision-Date: 2001-12-28 18:07+0100\n"
 "Last-Translator: Oystein Steimler <oystein at rexta.net>\n"
 "Language-Team: Norwegian (Bokmal) <oystein at rexta.net>\n"
@@ -14,559 +13,315 @@ msgstr ""
 "Content-Type: text/plain; charset=iso-8859-1\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: templates/annotate/header.inc:4
-msgid "#"
+msgid ""
+" This form allows you to request diffs between any two revisions of a file.  "
+"You may select a symbolic revision name using the selection box or you may "
+"type in a numeric name using the type-in text box."
 msgstr ""
+"I dette skjemaet kan du be om differ mellom to revisjoner av en fil. Du kan "
+"velge et symbolsk revisjonsnavn ved &aring; bruke valgboksen, eller du kan "
+"skrive inn et numerisk navn ved &aring; fylle ut tekstboksen."
 
-#: templates/patchsets/ps.inc:12 templates/log/rev.inc:26
-#, php-format
+#, c-format
 msgid "%s ago"
 msgstr "%s siden"
 
-#: templates/diff/hr/footer.inc:19
-#, php-format
+#, c-format
 msgid "Added in v.%s"
 msgstr "Lagt til v.%s"
 
-#: templates/log/request.inc:57
 msgid "All Branches"
 msgstr "Alle greiner"
 
-#: co.php:72 templates/log/rev.inc:8
 msgid "Annotate"
 msgstr "Annoter"
 
-#: templates/directory/header.inc:18 templates/stats/stats.inc:3
-#: templates/annotate/header.inc:5
 msgid "Author"
 msgstr "Forfatter"
 
-#: templates/directory/back.inc:4
 msgid "Back"
 msgstr "Tilbake"
 
-#: templates/checkout/checkout.inc:27 templates/log/rev.inc:31
 msgid "Branch Point for:"
 msgstr "Forgreiningspunkt for:"
 
-#: templates/checkout/checkout.inc:11 templates/patchsets/ps.inc:14
-#: templates/log/rev.inc:28
 msgid "Branch:"
 msgstr "Grein:"
 
-#: lib/Chora.php:465
-#, fuzzy
-msgid "Branches"
-msgstr "Alle greiner"
-
-#: templates/history/branch_cell.inc:2
 msgid "Branching to"
 msgstr "Forgreiner til"
 
-#: templates/checkout/checkout.inc:35
-#, php-format
-msgid "Changed since <b>%s</b>"
-msgstr "Forandret siden <b>%s</b>"
-
-#: templates/log/rev.inc:37
-#, fuzzy, php-format
-msgid "Changed since <strong>%s</strong>"
-msgstr "Forandret siden <b>%s</b>"
+#, c-format
+msgid "CVS Annotation of %s for version %s"
+msgstr "CVS annotasjon av %s for versjon %s"
 
-#: templates/history/rev.inc:8
-#, php-format
-msgid "Changed: %s"
-msgstr "Forandret: %s"
+#, c-format
+msgid "CVS Branching View for %s"
+msgstr "CVS Forgreiningsvisning for %s"
 
-#: co.php:70
-#, fuzzy, php-format
-msgid "Checkout of %s (revision %s)"
+#, c-format
+msgid "CVS Checkout of %s (revision %s)"
 msgstr "CVSutsjekking av %s (revisjon %s)"
 
-#: templates/cvsgraph/cvsgraph.inc:11
-msgid ""
-"Click on the links in between revisions to get a diff between those "
-"revisions."
-msgstr ""
-
-#: templates/cvsgraph/cvsgraph.inc:10
-msgid "Click on the revisions and branches to display the file."
-msgstr ""
+#, c-format
+msgid "CVS Directory of /%s"
+msgstr "CVSmappe for /%s"
 
-#: templates/log/rev.inc:42 templates/log/rev.inc:50
-msgid "Colored"
-msgstr ""
+#, c-format
+msgid "CVS Log for %s"
+msgstr "CVSlogg for %s"
 
-#: templates/checkout/checkout.inc:19 templates/patchsets/ps.inc:17
-#, fuzzy
-msgid "Commit Tags:"
+msgid "CVS Tags:"
 msgstr "CVSmerker:"
 
-#: templates/stats/stats.inc:4
-msgid "Commits"
-msgstr ""
+#, c-format
+msgid "Changed since <b>%s</b>"
+msgstr "Forandret siden <b>%s</b>"
+
+#, c-format
+msgid "Changed: %s"
+msgstr "Forandret: %s"
+
+msgid "Chora Homepage"
+msgstr "Chora hjemmeside"
+
+msgid "Chora is not properly configured"
+msgstr "Chora er ikke satt opp ordentlig"
 
-#: templates/log/request.inc:28
 msgid "Context"
 msgstr "Kontekst"
 
-#: config/prefs.php.dist:11
-msgid "Customize tasks to run upon logging in to Chora."
-msgstr ""
+msgid "Couldn't perform checkout of the requested file"
+msgstr "Kunne ikke uf&oslash;re utsjekking p&aring; de forespurte filene"
 
-#: templates/directory/header.inc:23
-#, fuzzy
-msgid "Da_te"
-msgstr "Dato"
-
-#: templates/directory/header.inc:23
 msgid "Date"
 msgstr "Dato"
 
-#: patchsets.php:66
-#, fuzzy
-msgid "Deleted"
-msgstr "Slettet fil"
-
-#: templates/directory/file.inc:5 templates/directory/file.inc:17
 msgid "Deleted File"
 msgstr "Slettet fil"
 
-#: templates/log/rev.inc:13
-#, fuzzy
-msgid "Deselect"
-msgstr "Velg bort"
-
-#: patchsets.php:63
-#, fuzzy
-msgid "Diff"
-msgstr "Hent differ"
-
-#: patchsets.php:46
-msgid "Diff All Files"
-msgstr ""
-
-#: diff.php:69
-#, php-format
+#, c-format
 msgid "Diff for %s between version %s and %s"
 msgstr "Diff for %s mellom versjon %s og %s"
 
-#: templates/log/rev.inc:49
-#, php-format
-msgid "Diffs to <strong>%s</strong>"
-msgstr ""
+#, c-format
+msgid "Diffs ignoring whitespace to version %s"
+msgstr "Differ (uten &aring; bry seg om blanke) til versjon %s"
+
+#, c-format
+msgid "Diffs to selected version %s"
+msgstr "Differ til valgt versjon %s"
 
-#: templates/log/rev.inc:41
-#, php-format
+#, c-format
 msgid "Diffs to version %s"
 msgstr "Differ til versjon %s"
 
-#: templates/directory/dir.inc:5 templates/directory/dir.inc:7
 msgid "Directory"
 msgstr "Mappe"
 
-#: annotate.php:30 co.php:73 templates/log/rev.inc:10
 msgid "Download"
 msgstr "Last ned"
 
-#: templates/log/request.inc:30
 msgid "Ed Script"
 msgstr "Ed Script"
 
-#: templates/error_page.inc:4
-msgid "Error"
-msgstr "Feil"
-
-#: templates/error_page.inc:7
 msgid "Error Encountered"
 msgstr "Det oppstod en feil"
 
-#: templates/directory/file.inc:7 templates/directory/file.inc:19
-#: templates/directory/header.inc:8
+msgid "Error"
+msgstr "Feil"
+
 msgid "File"
 msgstr "Fil"
 
-#: templates/patchsets/ps.inc:21
-#, fuzzy
-msgid "Files Changed:"
-msgstr "Forandret: %s"
-
-#: templates/log/request.inc:44
 msgid "Get Diffs"
 msgstr "Hent differ"
 
-#: lib/Chora.php:468
-msgid "Graph"
-msgstr ""
-
-#: cvsgraph.php:48
-#, php-format
-msgid "Graph for %s"
-msgstr ""
-
-#: browse.php:40
 msgid "Hide Deleted Files"
 msgstr "Skjul slettede filer"
 
-#: browse.php:40
-#, fuzzy
-msgid "Hide _Deleted Files"
-msgstr "Skjul slettede filer"
-
-#: templates/log/request.inc:26
 msgid "Human Readable"
 msgstr "Menneskelesbart"
 
-#: templates/directory/header.inc:25
-msgid "Last Log"
-msgstr "Siste logg"
+msgid "Invalid revision number"
+msgstr "Ugyldig revisjonsnummer"
 
-#: templates/diff/hr/header.inc:5
-#, php-format
+#, c-format
 msgid "Last Log Message for rev %s:"
 msgstr "Siste loggmelding for rev %s:"
 
-#: templates/diff/hr/footer.inc:5
-msgid "Legend:"
-msgstr ""
+msgid "Last Log Message:"
+msgstr "Siste loggmelding:"
 
-#: templates/annotate/header.inc:7
-msgid "Line"
-msgstr "Linje"
+msgid "Last Log"
+msgstr "Siste logg"
 
-#: templates/diff/hr/row.inc:3 templates/diff/hr/row.inc:7
-#, php-format
+#, c-format
 msgid "Line %s"
 msgstr "Linje %s"
 
-#: templates/headerbar.inc:11
+msgid "Line"
+msgstr "Linje"
+
 msgid "Location:"
 msgstr "Plassering"
 
-#: templates/checkout/checkout.inc:42
-msgid "Log:"
-msgstr ""
-
-#: config/prefs.php.dist:10
-msgid "Login Tasks"
-msgstr ""
-
-#: lib/Chora.php:459
-msgid "Logs"
-msgstr ""
-
-#: templates/log/rev.inc:43 templates/log/rev.inc:51
-#, fuzzy
-msgid "Long"
-msgstr "lang"
-
-#: diff.php:61
-msgid "Malformed Query"
-msgstr ""
-
-#: patchsets.php:59
-#, fuzzy
-msgid "New File"
-msgstr "Fil"
-
-#: templates/diff/hr/nochange.inc:6
 msgid "No Viewable Change"
 msgstr "Ingen synlige endringer"
 
-#: templates/log/rev.inc:44 templates/log/rev.inc:52
-#, fuzzy
-msgid "NoWhitespaceChanges"
-msgstr "Ingen synlige endringer"
-
-#: config/prefs.php.dist:9
-#, fuzzy
-msgid "Other Options"
-msgstr "Andre Repositories"
-
-#: lib/Chora.php:368
 msgid "Other Repositories"
 msgstr "Andre Repositories"
 
-#: templates/directory/back.inc:4
-#, fuzzy
-msgid "Parent Directory"
-msgstr "Forrige mappe"
-
-#: templates/patchsets/ps.inc:6
-#, php-format
-msgid "PatchSet %s</span> by %s"
-msgstr ""
-
-#: lib/Chora.php:462
-msgid "Patchsets"
-msgstr ""
-
-#: patchsets.php:27
-#, php-format
-msgid "Patchsets for %s"
-msgstr ""
-
-#: templates/error_page.inc:13
 msgid "Please contact"
 msgstr "Vennligst ta kontakt med"
 
-#: templates/diff/hr/footer.inc:11
-#, php-format
+msgid "Previous Directory"
+msgstr "Forrige mappe"
+
+#, c-format
 msgid "Removed in v.%s"
 msgstr "Fjernet i v.%s"
 
-#: templates/log/request.inc:14
 msgid "Retrieve diffs between:"
 msgstr "Hent differ mellom:"
 
-#: templates/directory/header.inc:13 templates/annotate/header.inc:6
 msgid "Rev"
 msgstr "Rev"
 
-#: co.php:77
-#, php-format
-msgid "Revision %s for file %s not found."
-msgstr ""
-
-#: templates/checkout/checkout.inc:4
-#, php-format
-msgid "Revision <b>%s</b>, <i>%s</i> (%s ago) by %s"
-msgstr ""
-
-#: templates/log/rev.inc:15
 msgid "Select for Diff"
 msgstr "Velg for diff"
 
-#: browse.php:42
 msgid "Show Deleted Files"
 msgstr "Vis slettede filer"
 
-#: browse.php:42
-#, fuzzy
-msgid "Show _Deleted Files"
-msgstr "Vis slettede filer"
-
-#: templates/log/request.inc:29
 msgid "Side-by-Side"
 msgstr "Side-om-side"
 
-#: templates/directory/header.inc:7 templates/directory/header.inc:12
-#: templates/directory/header.inc:17 templates/directory/header.inc:22
+msgid "Some of Chora's configuration files are missing:"
+msgstr "Noen av Choras konfigurasjonsfiler mangler:"
+
 msgid "Sort Order"
 msgstr "Sorteringsrekkef&oslash;lge"
 
-#: annotate.php:27
-#, fuzzy, php-format
-msgid "Source Annotation of %s for version %s"
-msgstr "CVS annotasjon av %s for versjon %s"
+msgid "Switch to Branch View"
+msgstr "Bytt til greinvisning"
 
-#: history.php:160
-#, fuzzy, php-format
-msgid "Source Branching View for %s"
-msgstr "CVS Forgreiningsvisning for %s"
+msgid "Switch to Log View"
+msgstr "Bytt til loggvisning"
 
-#: browse.php:32
-#, fuzzy, php-format
-msgid "Source Directory of /%s"
-msgstr "CVSmappe for /%s"
-
-#: browse.php:126
-#, fuzzy, php-format
-msgid "Source Log for %s"
-msgstr "CVSlogg for %s"
-
-#: lib/Chora.php:182
 msgid ""
-"SourceRoot not found! This could be a misconfiguration by the server "
-"administrator, or the server could be having temporary problems. Please try "
-"again later."
-msgstr ""
-
-#: lib/Chora.php:470
-msgid "Statistics"
+"This file controls the stylesheet that is used to set colors and fonts in "
+"addition to or overriding Horde defaults."
 msgstr ""
+"Denne filen kontrollerer stilarkene som bestemmer farger og skrifttyper. "
+"Filen overstyrer standard Horde instillinger."
 
-#: stats.php:27
-#, php-format
-msgid "Statistics for %s"
-msgstr ""
+msgid ""
+"This file defines all of the cvs repositories that you wish Chora to display."
+msgstr "Denne filen definerer alle CVSrepositories du vil at Chora skal vise."
 
-#: templates/log/rev.inc:34
-#, fuzzy
-msgid "Tags:"
-msgstr "CVSmerker:"
+msgid "This file defines any special MIME-type handling for Chora."
+msgstr "Denne filen definerer spesiell MIMEh&aring;ndtering for Chora."
 
-#: templates/log/request.inc:5
-#, fuzzy
 msgid ""
-"This form allows you to request diffs between any two revisions of a file.  "
-"You may select a symbolic revision name using the selection box or you may "
-"type in a numeric name using the type-in text box."
+"This is the main Chora configuration file. It contains paths and options for "
+"all Chora scripts."
 msgstr ""
-"I dette skjemaet kan du be om differ mellom to revisjoner av en fil. Du kan "
-"velge et symbolsk revisjonsnavn ved &aring; bruke valgboksen, eller du kan "
-"skrive inn et numerisk navn ved &aring; fylle ut tekstboksen."
+"Dette er hovedkonfigurasjonsfilen for Chora. Den inneholder stier og valg "
+"for alle Choraskriptene."
 
-#: templates/headerbar.inc:16
 msgid "Tracking Branch"
 msgstr "F&oslash;lg grein"
 
-#: templates/log/request.inc:23
 msgid "Type:"
 msgstr "Type:"
 
-#: templates/log/request.inc:27
 msgid "Unified"
 msgstr "Enhetlig"
 
-#: templates/log/request.inc:18 templates/log/request.inc:38
+msgid "Unselect"
+msgstr "Velg bort"
+
 msgid "Use Text Field"
 msgstr "Bruk tekstfelt"
 
-#: config/prefs.php.dist:37
-msgid "Use last viewed file or directory at login time"
-msgstr ""
-
-#: annotate.php:29 templates/log/rev.inc:9
-msgid "View"
-msgstr "Vis"
-
-#: templates/log/request.inc:60
 msgid "View Branch"
 msgstr "Vis grein"
 
-#: templates/log/request.inc:54
 msgid "View revisions on:"
 msgstr "Vis revisjoner av:"
 
-#: lib/Chora.php:472
-#, fuzzy
-msgid "View:"
+msgid "View"
 msgstr "Vis"
 
-#: templates/directory/header.inc:18
-#, fuzzy
-msgid "_Author"
-msgstr "Forfatter"
-
-#: lib/Chora.php:465
-#, fuzzy
-msgid "_Branches"
-msgstr "Alle greiner"
-
-#: lib/Chora.php:439
-msgid "_Browse"
-msgstr ""
-
-#: templates/directory/header.inc:8
-#, fuzzy
-msgid "_File"
-msgstr "Fil"
-
-#: lib/Chora.php:468
-msgid "_Graph"
-msgstr ""
-
-#: lib/Chora.php:459
-msgid "_Logs"
-msgstr ""
-
-#: lib/Chora.php:462
-msgid "_Patchsets"
-msgstr ""
-
-#: templates/directory/header.inc:13
-#, fuzzy
-msgid "_Rev"
-msgstr "Rev"
-
-#: lib/Chora.php:470
-msgid "_Statistics"
-msgstr ""
-
-#: templates/log/request.inc:35
 msgid "and:"
 msgstr "og: "
 
-#: templates/history/rev.inc:4 templates/log/rev.inc:5
-#, php-format
+#, c-format
 msgid "by %s"
 msgstr "etter %s"
 
-#: templates/diff/hr/footer.inc:15
 msgid "changed lines"
 msgstr "endrede linjer"
 
-#: lib/Chora.php:31
 msgid "day"
 msgstr "dag"
 
-#: lib/Chora.php:31
 msgid "days"
 msgstr "dager"
 
-#: templates/error_page.inc:14
 msgid "for further information."
 msgstr "for n&aelig;rmere informasjon."
 
-#: lib/Chora.php:30
 msgid "hour"
 msgstr "time"
 
-#: lib/Chora.php:30
 msgid "hours"
 msgstr "timer"
 
-#: lib/Chora.php:29
+msgid "long"
+msgstr "lang"
+
 msgid "minute"
 msgstr "minutt"
 
-#: lib/Chora.php:29
 msgid "minutes"
 msgstr "minutter"
 
-#: lib/Chora.php:33
 msgid "month"
 msgstr "m&aring;ned"
 
-#: lib/Chora.php:33
 msgid "months"
 msgstr "m&aring;neder"
 
-#: templates/history/branch_cell.inc:6
-#, php-format
+#, c-format
 msgid "revision %s"
 msgstr "revisjon %s"
 
-#: lib/Chora.php:28
 msgid "second"
 msgstr "sekund"
 
-#: lib/Chora.php:28
 msgid "seconds"
 msgstr "sekunder"
 
-#: templates/diff/hr/header.inc:13 templates/diff/hr/header.inc:14
-#, php-format
+msgid "unified"
+msgstr "enhetlig"
+
+#, c-format
 msgid "version %s"
 msgstr "versjon %s"
 
-#: lib/Chora.php:42
 msgid "very little time"
 msgstr "veldig lite tid"
 
-#: lib/Chora.php:32
 msgid "week"
 msgstr "uke"
 
-#: lib/Chora.php:32
 msgid "weeks"
 msgstr "uker"
 
-#: lib/Chora.php:34
 msgid "year"
 msgstr "&aring;r"
 
-#: lib/Chora.php:34
 msgid "years"
 msgstr "&aring;r"
diff --git a/po/nl_NL.po b/po/nl_NL.po
index 22ca964..2b5ec8b 100644
--- a/po/nl_NL.po
+++ b/po/nl_NL.po
@@ -1,12 +1,12 @@
 # Chora Dutch translation.
-# Copyright (C) 2001 Jan Kuipers.
+# Copyright 2001 Jan Kuipers.
 # Jan Kuipers <jrkuipers at lauwerscollege.nl>, 2001.
 #
 msgid ""
 msgstr ""
 "Project-Id-Version: Chora 2.0-cvs\n"
 "Report-Msgid-Bugs-To: dev at lists.horde.org\n"
-"POT-Creation-Date: 2005-10-13 17:16+0200\n"
+"POT-Creation-Date: 2004-11-27 20:36+0100\n"
 "PO-Revision-Date: 2004-11-27 21:39+0100\n"
 "Last-Translator: Jan Kuipers <jrkuipers at lauwerscollege.nl>\n"
 "Language-Team: Dutch <i18n at lists.horde.org>\n"
@@ -18,7 +18,7 @@ msgstr ""
 msgid "#"
 msgstr "#"
 
-#: templates/patchsets/ps.inc:12 templates/log/rev.inc:26
+#: templates/patchsets/ps.inc:12 templates/log/rev.inc:24
 #, php-format
 msgid "%s ago"
 msgstr "%s geleden"
@@ -32,11 +32,11 @@ msgstr "Toegevoegd in v.%s"
 msgid "All Branches"
 msgstr "Alle vertakkingen"
 
-#: co.php:72 templates/log/rev.inc:8
+#: co.php:68 templates/log/rev.inc:8
 msgid "Annotate"
 msgstr "Annoteren"
 
-#: templates/directory/header.inc:18 templates/stats/stats.inc:3
+#: templates/stats/stats.inc:3 templates/directory/header.inc:18
 #: templates/annotate/header.inc:5
 msgid "Author"
 msgstr "Auteur"
@@ -45,16 +45,16 @@ msgstr "Auteur"
 msgid "Back"
 msgstr "Terug"
 
-#: templates/checkout/checkout.inc:27 templates/log/rev.inc:31
+#: templates/log/rev.inc:29 templates/checkout/checkout.inc:27
 msgid "Branch Point for:"
 msgstr "Vertakkingspunt voor:"
 
-#: templates/checkout/checkout.inc:11 templates/patchsets/ps.inc:14
-#: templates/log/rev.inc:28
+#: templates/patchsets/ps.inc:14 templates/log/rev.inc:26
+#: templates/checkout/checkout.inc:11
 msgid "Branch:"
 msgstr "Vertakking:"
 
-#: lib/Chora.php:465
+#: lib/Chora.php:467
 msgid "Branches"
 msgstr "Vertakkingen"
 
@@ -62,22 +62,17 @@ msgstr "Vertakkingen"
 msgid "Branching to"
 msgstr "Vertakt naar:"
 
-#: templates/checkout/checkout.inc:35
+#: templates/log/rev.inc:35 templates/checkout/checkout.inc:35
 #, php-format
 msgid "Changed since <b>%s</b>"
 msgstr "Gewijzigd sinds <b>%s</b>"
 
-#: templates/log/rev.inc:37
-#, fuzzy, php-format
-msgid "Changed since <strong>%s</strong>"
-msgstr "Gewijzigd sinds <b>%s</b>"
-
 #: templates/history/rev.inc:8
 #, php-format
 msgid "Changed: %s"
 msgstr "Gewijzigd: %s"
 
-#: co.php:70
+#: co.php:66
 #, php-format
 msgid "Checkout of %s (revision %s)"
 msgstr "Controle van %s (revisie %s)"
@@ -94,11 +89,11 @@ msgstr ""
 msgid "Click on the revisions and branches to display the file."
 msgstr "Klik op de revisies en vertakkingen om een bestand weer te geven."
 
-#: templates/log/rev.inc:42 templates/log/rev.inc:50
+#: templates/log/rev.inc:40 templates/log/rev.inc:49
 msgid "Colored"
 msgstr "Gekleurd"
 
-#: templates/checkout/checkout.inc:19 templates/patchsets/ps.inc:17
+#: templates/patchsets/ps.inc:17 templates/checkout/checkout.inc:19
 msgid "Commit Tags:"
 msgstr "Commit Tags:"
 
@@ -127,7 +122,7 @@ msgstr "Datum"
 msgid "Deleted"
 msgstr "Verwijderd"
 
-#: templates/directory/file.inc:5 templates/directory/file.inc:17
+#: templates/directory/file.inc:6 templates/directory/file.inc:18
 msgid "Deleted File"
 msgstr "Bestand verwijderd"
 
@@ -148,12 +143,12 @@ msgstr "Verschillen van alle bestanden"
 msgid "Diff for %s between version %s and %s"
 msgstr "Verschil in %s tussen versie %s en %s"
 
-#: templates/log/rev.inc:49
-#, fuzzy, php-format
-msgid "Diffs to <strong>%s</strong>"
+#: templates/log/rev.inc:48
+#, php-format
+msgid "Diffs to <b>%s</b>"
 msgstr "Verschillen in <b>%s</b>"
 
-#: templates/log/rev.inc:41
+#: templates/log/rev.inc:39
 #, php-format
 msgid "Diffs to version %s"
 msgstr "Verschillen in versie %s"
@@ -162,7 +157,7 @@ msgstr "Verschillen in versie %s"
 msgid "Directory"
 msgstr "Directory"
 
-#: annotate.php:30 co.php:73 templates/log/rev.inc:10
+#: annotate.php:30 co.php:69 templates/log/rev.inc:10
 msgid "Download"
 msgstr "Downloaden"
 
@@ -178,7 +173,7 @@ msgstr "Fout"
 msgid "Error Encountered"
 msgstr "Er heeft zich een fout voorgedaan"
 
-#: templates/directory/file.inc:7 templates/directory/file.inc:19
+#: templates/directory/file.inc:8 templates/directory/file.inc:20
 #: templates/directory/header.inc:8
 msgid "File"
 msgstr "Bestand"
@@ -191,7 +186,7 @@ msgstr "Bestanden gewijzigd:"
 msgid "Get Diffs"
 msgstr "Haal verschillen"
 
-#: lib/Chora.php:468
+#: lib/Chora.php:470
 msgid "Graph"
 msgstr "Grafiek"
 
@@ -246,11 +241,11 @@ msgstr "Log:"
 msgid "Login Tasks"
 msgstr "Aanmeldtaken"
 
-#: lib/Chora.php:459
+#: lib/Chora.php:461
 msgid "Logs"
 msgstr "Logs"
 
-#: templates/log/rev.inc:43 templates/log/rev.inc:51
+#: templates/log/rev.inc:41 templates/log/rev.inc:50
 msgid "Long"
 msgstr "Lang"
 
@@ -266,7 +261,7 @@ msgstr "Nieuw bestand"
 msgid "No Viewable Change"
 msgstr "Geen weer te geven wijziging"
 
-#: templates/log/rev.inc:44 templates/log/rev.inc:52
+#: templates/log/rev.inc:42 templates/log/rev.inc:51
 msgid "NoWhitespaceChanges"
 msgstr "Geen witruimtewijzigingen"
 
@@ -274,7 +269,7 @@ msgstr "Geen witruimtewijzigingen"
 msgid "Other Options"
 msgstr "Andere opties"
 
-#: lib/Chora.php:368
+#: lib/Chora.php:369
 msgid "Other Repositories"
 msgstr "Andere depots"
 
@@ -287,7 +282,7 @@ msgstr "Voorgaande directory"
 msgid "PatchSet %s</span> by %s"
 msgstr "Patchset %s</span> door %s"
 
-#: lib/Chora.php:462
+#: lib/Chora.php:464
 msgid "Patchsets"
 msgstr "Patchsets"
 
@@ -313,7 +308,7 @@ msgstr "Haal verschillen op tussen:"
 msgid "Rev"
 msgstr "Revisie"
 
-#: co.php:77
+#: co.php:73
 #, php-format
 msgid "Revision %s for file %s not found."
 msgstr "Revisie %s van bestand %s niet gevonden."
@@ -364,7 +359,7 @@ msgstr "Bron directory van /%s"
 msgid "Source Log for %s"
 msgstr "Bron log voor %s"
 
-#: lib/Chora.php:182
+#: lib/Chora.php:183
 msgid ""
 "SourceRoot not found! This could be a misconfiguration by the server "
 "administrator, or the server could be having temporary problems. Please try "
@@ -374,7 +369,7 @@ msgstr ""
 "zijn, of de server kan tijdelijke problemen hebben. Probeer later opnieuw, a."
 "u.b."
 
-#: lib/Chora.php:470
+#: lib/Chora.php:472
 msgid "Statistics"
 msgstr "Statistieken"
 
@@ -383,7 +378,7 @@ msgstr "Statistieken"
 msgid "Statistics for %s"
 msgstr "Statistieken voor %s"
 
-#: templates/log/rev.inc:34
+#: templates/log/rev.inc:32
 msgid "Tags:"
 msgstr "Tags:"
 
@@ -429,7 +424,7 @@ msgstr "Vertakking weergeven"
 msgid "View revisions on:"
 msgstr "Revisies weergeven van:"
 
-#: lib/Chora.php:472
+#: lib/Chora.php:474
 msgid "View:"
 msgstr "Weergeven:"
 
@@ -437,11 +432,11 @@ msgstr "Weergeven:"
 msgid "_Author"
 msgstr "_Auteur"
 
-#: lib/Chora.php:465
+#: lib/Chora.php:467
 msgid "_Branches"
 msgstr "Verta_kkingen"
 
-#: lib/Chora.php:439
+#: lib/Chora.php:441
 msgid "_Browse"
 msgstr "_Bladeren"
 
@@ -449,15 +444,15 @@ msgstr "_Bladeren"
 msgid "_File"
 msgstr "Be_stand"
 
-#: lib/Chora.php:468
+#: lib/Chora.php:470
 msgid "_Graph"
 msgstr "_Grafiek"
 
-#: lib/Chora.php:459
+#: lib/Chora.php:461
 msgid "_Logs"
 msgstr "_Logs"
 
-#: lib/Chora.php:462
+#: lib/Chora.php:464
 msgid "_Patchsets"
 msgstr "_Patchsets"
 
@@ -465,7 +460,7 @@ msgstr "_Patchsets"
 msgid "_Rev"
 msgstr "_Revisie"
 
-#: lib/Chora.php:470
+#: lib/Chora.php:472
 msgid "_Statistics"
 msgstr "Stat_istieken"
 
@@ -473,7 +468,7 @@ msgstr "Stat_istieken"
 msgid "and:"
 msgstr "en:"
 
-#: templates/history/rev.inc:4 templates/log/rev.inc:5
+#: templates/log/rev.inc:5 templates/history/rev.inc:4
 #, php-format
 msgid "by %s"
 msgstr "door %s"
@@ -482,11 +477,11 @@ msgstr "door %s"
 msgid "changed lines"
 msgstr "gewijzigde regels"
 
-#: lib/Chora.php:31
+#: lib/Chora.php:32
 msgid "day"
 msgstr "dag"
 
-#: lib/Chora.php:31
+#: lib/Chora.php:32
 msgid "days"
 msgstr "dagen"
 
@@ -494,27 +489,27 @@ msgstr "dagen"
 msgid "for further information."
 msgstr "voor verdere informatie."
 
-#: lib/Chora.php:30
+#: lib/Chora.php:31
 msgid "hour"
 msgstr "uur"
 
-#: lib/Chora.php:30
+#: lib/Chora.php:31
 msgid "hours"
 msgstr "uren"
 
-#: lib/Chora.php:29
+#: lib/Chora.php:30
 msgid "minute"
 msgstr "minuut"
 
-#: lib/Chora.php:29
+#: lib/Chora.php:30
 msgid "minutes"
 msgstr "minuten"
 
-#: lib/Chora.php:33
+#: lib/Chora.php:34
 msgid "month"
 msgstr "maand"
 
-#: lib/Chora.php:33
+#: lib/Chora.php:34
 msgid "months"
 msgstr "maanden"
 
@@ -523,11 +518,11 @@ msgstr "maanden"
 msgid "revision %s"
 msgstr "revisie %s"
 
-#: lib/Chora.php:28
+#: lib/Chora.php:29
 msgid "second"
 msgstr "seconde"
 
-#: lib/Chora.php:28
+#: lib/Chora.php:29
 msgid "seconds"
 msgstr "seconden"
 
@@ -536,22 +531,22 @@ msgstr "seconden"
 msgid "version %s"
 msgstr "versie %s"
 
-#: lib/Chora.php:42
+#: lib/Chora.php:43
 msgid "very little time"
 msgstr "erg kort"
 
-#: lib/Chora.php:32
+#: lib/Chora.php:33
 msgid "week"
 msgstr "week"
 
-#: lib/Chora.php:32
+#: lib/Chora.php:33
 msgid "weeks"
 msgstr "weken"
 
-#: lib/Chora.php:34
+#: lib/Chora.php:35
 msgid "year"
 msgstr "jaar"
 
-#: lib/Chora.php:34
+#: lib/Chora.php:35
 msgid "years"
 msgstr "jaren"
diff --git a/po/pl_PL.po b/po/pl_PL.po
index 0f773fa..4e110b8 100644
--- a/po/pl_PL.po
+++ b/po/pl_PL.po
@@ -1,12 +1,11 @@
 # Chora Polish translation.
-# Copyright (C) 2001 Piotr Roszatycki <dexter at debian.org>
+# Copyright 2001 Piotr Roszatycki <dexter at debian.org>
 # Piotr Roszatycki <dexter at debian.org>, 2001.
 #
 msgid ""
 msgstr ""
 "Project-Id-Version: Chora 1.0\n"
-"Report-Msgid-Bugs-To: dev at lists.horde.org\n"
-"POT-Creation-Date: 2005-10-13 17:16+0200\n"
+"POT-Creation-Date: 2001-12-30 14:53+0100\n"
 "PO-Revision-Date: 2001-12-30 14:15+0100\n"
 "Last-Translator: Piotr Roszatycki <dexter at debian.org>\n"
 "Language-Team: Polish <dev at horde.org>\n"
@@ -14,560 +13,316 @@ msgstr ""
 "Content-Type: text/plain; charset=iso-8859-2\n"
 "Content-Transfer-Encoding: 8-bit\n"
 
-#: templates/annotate/header.inc:4
-msgid "#"
+msgid ""
+" This form allows you to request diffs between any two revisions of a file.  "
+"You may select a symbolic revision name using the selection box or you may "
+"type in a numeric name using the type-in text box."
 msgstr ""
+" Ten formularz pozwala na utworzenie pliku zmian pomiêdzy dowolnymi wersjami "
+"tego pliku. Mo¿esz okre¶liæ symboliczne oznaczenie wersji poprzez wybranie "
+"oznaczenia z listy. Mo¿esz te¿ okre¶liæ numerycznie wersjê poprzez wpisanie "
+"oznaczenia w pole tekstowe."
 
-#: templates/patchsets/ps.inc:12 templates/log/rev.inc:26
-#, php-format
+#, c-format
 msgid "%s ago"
 msgstr "%s temu"
 
-#: templates/diff/hr/footer.inc:19
-#, php-format
+#, c-format
 msgid "Added in v.%s"
 msgstr "Dodane w v.%s"
 
-#: templates/log/request.inc:57
 msgid "All Branches"
 msgstr "Wszystkie odga³êzienia"
 
-#: co.php:72 templates/log/rev.inc:8
 msgid "Annotate"
 msgstr "Adnotacja"
 
-#: templates/directory/header.inc:18 templates/stats/stats.inc:3
-#: templates/annotate/header.inc:5
 msgid "Author"
 msgstr "Autor"
 
-#: templates/directory/back.inc:4
 msgid "Back"
 msgstr "Cofnij"
 
-#: templates/checkout/checkout.inc:27 templates/log/rev.inc:31
 msgid "Branch Point for:"
 msgstr "Punkt odga³êzienia dla:"
 
-#: templates/checkout/checkout.inc:11 templates/patchsets/ps.inc:14
-#: templates/log/rev.inc:28
 msgid "Branch:"
 msgstr "Odga³êzienie:"
 
-#: lib/Chora.php:465
-#, fuzzy
-msgid "Branches"
-msgstr "Wszystkie odga³êzienia"
-
-#: templates/history/branch_cell.inc:2
 msgid "Branching to"
 msgstr "Odga³êzienie do"
 
-#: templates/checkout/checkout.inc:35
-#, php-format
-msgid "Changed since <b>%s</b>"
-msgstr "Zmiana od <b>%s</b>"
-
-#: templates/log/rev.inc:37
-#, fuzzy, php-format
-msgid "Changed since <strong>%s</strong>"
-msgstr "Zmiana od <b>%s</b>"
+#, c-format
+msgid "CVS Annotation of %s for version %s"
+msgstr "Adnotacja CVS dla %s dla wersji %s"
 
-#: templates/history/rev.inc:8
-#, php-format
-msgid "Changed: %s"
-msgstr "Zmiana: %s"
+#, c-format
+msgid "CVS Branching View for %s"
+msgstr "Widok odga³êzieñ CVS dla %s"
 
-#: co.php:70
-#, fuzzy, php-format
-msgid "Checkout of %s (revision %s)"
+#, c-format
+msgid "CVS Checkout of %s (revision %s)"
 msgstr "Aktualizacja CVS dla %s (wersja %s)"
 
-#: templates/cvsgraph/cvsgraph.inc:11
-msgid ""
-"Click on the links in between revisions to get a diff between those "
-"revisions."
-msgstr ""
-
-#: templates/cvsgraph/cvsgraph.inc:10
-msgid "Click on the revisions and branches to display the file."
-msgstr ""
+#, c-format
+msgid "CVS Directory of /%s"
+msgstr "Katalog CVS dla /%s"
 
-#: templates/log/rev.inc:42 templates/log/rev.inc:50
-msgid "Colored"
-msgstr ""
+#, c-format
+msgid "CVS Log for %s"
+msgstr "Historia CVS dla %s"
 
-#: templates/checkout/checkout.inc:19 templates/patchsets/ps.inc:17
-#, fuzzy
-msgid "Commit Tags:"
+msgid "CVS Tags:"
 msgstr "Oznaczenia CVS:"
 
-#: templates/stats/stats.inc:4
-msgid "Commits"
-msgstr ""
+#, c-format
+msgid "Changed since <b>%s</b>"
+msgstr "Zmiana od <b>%s</b>"
+
+#, c-format
+msgid "Changed: %s"
+msgstr "Zmiana: %s"
+
+msgid "Chora Homepage"
+msgstr "Strona domowa Chora"
+
+msgid "Chora is not properly configured"
+msgstr "Chora nie jest w pe³ni skonfigurowana."
 
-#: templates/log/request.inc:28
 msgid "Context"
 msgstr "Kontekst"
 
-#: config/prefs.php.dist:11
-msgid "Customize tasks to run upon logging in to Chora."
-msgstr ""
+msgid "Couldn't perform checkout of the requested file"
+msgstr "Nie mo¿na aktualizowaæ wybranego pliku"
 
-#: templates/directory/header.inc:23
-#, fuzzy
-msgid "Da_te"
-msgstr "Data"
-
-#: templates/directory/header.inc:23
 msgid "Date"
 msgstr "Data"
 
-#: patchsets.php:66
-#, fuzzy
-msgid "Deleted"
-msgstr "Usuniête pliki"
-
-#: templates/directory/file.inc:5 templates/directory/file.inc:17
 msgid "Deleted File"
 msgstr "Usuniête pliki"
 
-#: templates/log/rev.inc:13
-#, fuzzy
-msgid "Deselect"
-msgstr "Odznacz"
-
-#: patchsets.php:63
-#, fuzzy
-msgid "Diff"
-msgstr "Pobierz plik ró¿nicy"
-
-#: patchsets.php:46
-msgid "Diff All Files"
-msgstr ""
-
-#: diff.php:69
-#, php-format
+#, c-format
 msgid "Diff for %s between version %s and %s"
 msgstr "Plik ró¿nicy dla %s pomiêdzy wersjami %s i %s"
 
-#: templates/log/rev.inc:49
-#, php-format
-msgid "Diffs to <strong>%s</strong>"
-msgstr ""
+#, c-format
+msgid "Diffs ignoring whitespace to version %s"
+msgstr "Plik ró¿nicy z pominiêciem spacji dla wersji %s"
+
+#, c-format
+msgid "Diffs to selected version %s"
+msgstr "Plik ró¿nicy dla wybranej wersji %s"
 
-#: templates/log/rev.inc:41
-#, php-format
+#, c-format
 msgid "Diffs to version %s"
 msgstr "Plik ró¿nicy dla wersji %s"
 
-#: templates/directory/dir.inc:5 templates/directory/dir.inc:7
 msgid "Directory"
 msgstr "Ksi±¿ka adresowa"
 
-#: annotate.php:30 co.php:73 templates/log/rev.inc:10
 msgid "Download"
 msgstr "Pobierz"
 
-#: templates/log/request.inc:30
 msgid "Ed Script"
 msgstr "Edytuj skrypt"
 
-#: templates/error_page.inc:4
-msgid "Error"
-msgstr "B³±d"
-
-#: templates/error_page.inc:7
 msgid "Error Encountered"
 msgstr "Wyst±pi³ b³±d"
 
-#: templates/directory/file.inc:7 templates/directory/file.inc:19
-#: templates/directory/header.inc:8
+msgid "Error"
+msgstr "B³±d"
+
 msgid "File"
 msgstr "Plik"
 
-#: templates/patchsets/ps.inc:21
-#, fuzzy
-msgid "Files Changed:"
-msgstr "Zmiana: %s"
-
-#: templates/log/request.inc:44
 msgid "Get Diffs"
 msgstr "Pobierz plik ró¿nicy"
 
-#: lib/Chora.php:468
-msgid "Graph"
-msgstr ""
-
-#: cvsgraph.php:48
-#, php-format
-msgid "Graph for %s"
-msgstr ""
-
-#: browse.php:40
 msgid "Hide Deleted Files"
 msgstr "Ukryj usuniête pliki"
 
-#: browse.php:40
-#, fuzzy
-msgid "Hide _Deleted Files"
-msgstr "Ukryj usuniête pliki"
-
-#: templates/log/request.inc:26
 msgid "Human Readable"
 msgstr "Czytelne dla cz³owieka"
 
-#: templates/directory/header.inc:25
-msgid "Last Log"
-msgstr "Ostatni wpis"
+msgid "Invalid revision number"
+msgstr "B³êdny numer wersji"
 
-#: templates/diff/hr/header.inc:5
-#, php-format
+#, c-format
 msgid "Last Log Message for rev %s:"
 msgstr "Ostatni wpis w historii dla wersji %s:"
 
-#: templates/diff/hr/footer.inc:5
-msgid "Legend:"
-msgstr ""
+msgid "Last Log Message:"
+msgstr "Ostatni wpis w historii:"
 
-#: templates/annotate/header.inc:7
-msgid "Line"
-msgstr "Linia"
+msgid "Last Log"
+msgstr "Ostatni wpis"
 
-#: templates/diff/hr/row.inc:3 templates/diff/hr/row.inc:7
-#, php-format
+#, c-format
 msgid "Line %s"
 msgstr "Linia %s"
 
-#: templates/headerbar.inc:11
+msgid "Line"
+msgstr "Linia"
+
 msgid "Location:"
 msgstr "Lokacja:"
 
-#: templates/checkout/checkout.inc:42
-msgid "Log:"
-msgstr ""
-
-#: config/prefs.php.dist:10
-msgid "Login Tasks"
-msgstr ""
-
-#: lib/Chora.php:459
-msgid "Logs"
-msgstr ""
-
-#: templates/log/rev.inc:43 templates/log/rev.inc:51
-#, fuzzy
-msgid "Long"
-msgstr "d³ugi"
-
-#: diff.php:61
-msgid "Malformed Query"
-msgstr ""
-
-#: patchsets.php:59
-#, fuzzy
-msgid "New File"
-msgstr "Plik"
-
-#: templates/diff/hr/nochange.inc:6
 msgid "No Viewable Change"
 msgstr "Brak widocznych zmian"
 
-#: templates/log/rev.inc:44 templates/log/rev.inc:52
-#, fuzzy
-msgid "NoWhitespaceChanges"
-msgstr "Brak widocznych zmian"
-
-#: config/prefs.php.dist:9
-#, fuzzy
-msgid "Other Options"
-msgstr "Pozosta³e repozytoria"
-
-#: lib/Chora.php:368
 msgid "Other Repositories"
 msgstr "Pozosta³e repozytoria"
 
-#: templates/directory/back.inc:4
-#, fuzzy
-msgid "Parent Directory"
-msgstr "Poprzedni katalog"
-
-#: templates/patchsets/ps.inc:6
-#, php-format
-msgid "PatchSet %s</span> by %s"
-msgstr ""
-
-#: lib/Chora.php:462
-msgid "Patchsets"
-msgstr ""
-
-#: patchsets.php:27
-#, php-format
-msgid "Patchsets for %s"
-msgstr ""
-
-#: templates/error_page.inc:13
 msgid "Please contact"
 msgstr "Proszê skontaktowaæ siê"
 
-#: templates/diff/hr/footer.inc:11
-#, php-format
+msgid "Previous Directory"
+msgstr "Poprzedni katalog"
+
+#, c-format
 msgid "Removed in v.%s"
 msgstr "Usuniête w v.%s"
 
-#: templates/log/request.inc:14
 msgid "Retrieve diffs between:"
 msgstr "Pobierz plik zmian pomiêdzy:"
 
-#: templates/directory/header.inc:13 templates/annotate/header.inc:6
 msgid "Rev"
 msgstr "Wer"
 
-#: co.php:77
-#, php-format
-msgid "Revision %s for file %s not found."
-msgstr ""
-
-#: templates/checkout/checkout.inc:4
-#, php-format
-msgid "Revision <b>%s</b>, <i>%s</i> (%s ago) by %s"
-msgstr ""
-
-#: templates/log/rev.inc:15
 msgid "Select for Diff"
 msgstr "Wybierz folder"
 
-#: browse.php:42
 msgid "Show Deleted Files"
 msgstr "Poka¿ usuniête pliki"
 
-#: browse.php:42
-#, fuzzy
-msgid "Show _Deleted Files"
-msgstr "Poka¿ usuniête pliki"
-
-#: templates/log/request.inc:29
 msgid "Side-by-Side"
 msgstr "Obok siebie"
 
-#: templates/directory/header.inc:7 templates/directory/header.inc:12
-#: templates/directory/header.inc:17 templates/directory/header.inc:22
+msgid "Some of Chora's configuration files are missing:"
+msgstr "Brakuje kilku plików konfiguracyjnych Chora:"
+
 msgid "Sort Order"
 msgstr "Porz±dek sortowania"
 
-#: annotate.php:27
-#, fuzzy, php-format
-msgid "Source Annotation of %s for version %s"
-msgstr "Adnotacja CVS dla %s dla wersji %s"
+msgid "Switch to Branch View"
+msgstr "Prze³±cz na widok odga³êzieñ"
 
-#: history.php:160
-#, fuzzy, php-format
-msgid "Source Branching View for %s"
-msgstr "Widok odga³êzieñ CVS dla %s"
+msgid "Switch to Log View"
+msgstr "Prze³±cz na widok historii"
 
-#: browse.php:32
-#, fuzzy, php-format
-msgid "Source Directory of /%s"
-msgstr "Katalog CVS dla /%s"
-
-#: browse.php:126
-#, fuzzy, php-format
-msgid "Source Log for %s"
-msgstr "Historia CVS dla %s"
-
-#: lib/Chora.php:182
 msgid ""
-"SourceRoot not found! This could be a misconfiguration by the server "
-"administrator, or the server could be having temporary problems. Please try "
-"again later."
-msgstr ""
+"This file controls the stylesheet that is used to set colors and fonts in "
+"addition to or overriding Horde defaults."
+msgstr "W tym pliku okre¶lone s± style u¿ywane do ustawienia kolorów i fontów."
 
-#: lib/Chora.php:470
-msgid "Statistics"
+msgid ""
+"This file defines all of the cvs repositories that you wish Chora to display."
 msgstr ""
+"W tym pliku okre¶lone s± repozytoria CVS, które pokazywane s± przez Chora."
 
-#: stats.php:27
-#, php-format
-msgid "Statistics for %s"
+msgid "This file defines any special MIME-type handling for Chora."
 msgstr ""
+"W tym pliku okre¶lone s± skojarzenia typów MIME obs³ugiwane przez Chora."
 
-#: templates/log/rev.inc:34
-#, fuzzy
-msgid "Tags:"
-msgstr "Oznaczenia CVS:"
-
-#: templates/log/request.inc:5
-#, fuzzy
 msgid ""
-"This form allows you to request diffs between any two revisions of a file.  "
-"You may select a symbolic revision name using the selection box or you may "
-"type in a numeric name using the type-in text box."
+"This is the main Chora configuration file. It contains paths and options for "
+"all Chora scripts."
 msgstr ""
-" Ten formularz pozwala na utworzenie pliku zmian pomiêdzy dowolnymi wersjami "
-"tego pliku. Mo¿esz okre¶liæ symboliczne oznaczenie wersji poprzez wybranie "
-"oznaczenia z listy. Mo¿esz te¿ okre¶liæ numerycznie wersjê poprzez wpisanie "
-"oznaczenia w pole tekstowe."
+"To jest g³ówny plik konfiguracyjny Chora. Zawiera on ¶cie¿ki i opcje "
+"skryptów Chora."
 
-#: templates/headerbar.inc:16
 msgid "Tracking Branch"
 msgstr "¦ledzenie ga³êzi"
 
-#: templates/log/request.inc:23
 msgid "Type:"
 msgstr "Format:"
 
-#: templates/log/request.inc:27
 msgid "Unified"
 msgstr "Ujednolicony"
 
-#: templates/log/request.inc:18 templates/log/request.inc:38
+msgid "Unselect"
+msgstr "Odznacz"
+
 msgid "Use Text Field"
 msgstr "U¿yj pola tekstowego"
 
-#: config/prefs.php.dist:37
-msgid "Use last viewed file or directory at login time"
-msgstr ""
-
-#: annotate.php:29 templates/log/rev.inc:9
-msgid "View"
-msgstr "Widok"
-
-#: templates/log/request.inc:60
 msgid "View Branch"
 msgstr "Widok odga³êzieñ"
 
-#: templates/log/request.inc:54
 msgid "View revisions on:"
 msgstr "Widok wersji dla:"
 
-#: lib/Chora.php:472
-#, fuzzy
-msgid "View:"
+msgid "View"
 msgstr "Widok"
 
-#: templates/directory/header.inc:18
-#, fuzzy
-msgid "_Author"
-msgstr "Autor"
-
-#: lib/Chora.php:465
-#, fuzzy
-msgid "_Branches"
-msgstr "Wszystkie odga³êzienia"
-
-#: lib/Chora.php:439
-msgid "_Browse"
-msgstr ""
-
-#: templates/directory/header.inc:8
-#, fuzzy
-msgid "_File"
-msgstr "Plik"
-
-#: lib/Chora.php:468
-msgid "_Graph"
-msgstr ""
-
-#: lib/Chora.php:459
-msgid "_Logs"
-msgstr ""
-
-#: lib/Chora.php:462
-msgid "_Patchsets"
-msgstr ""
-
-#: templates/directory/header.inc:13
-#, fuzzy
-msgid "_Rev"
-msgstr "Wer"
-
-#: lib/Chora.php:470
-msgid "_Statistics"
-msgstr ""
-
-#: templates/log/request.inc:35
 msgid "and:"
 msgstr "i:"
 
-#: templates/history/rev.inc:4 templates/log/rev.inc:5
-#, php-format
+#, c-format
 msgid "by %s"
 msgstr "od %s"
 
-#: templates/diff/hr/footer.inc:15
 msgid "changed lines"
 msgstr "zmienione linie"
 
-#: lib/Chora.php:31
 msgid "day"
 msgstr "dzieñ"
 
-#: lib/Chora.php:31
 msgid "days"
 msgstr "dni"
 
-#: templates/error_page.inc:14
 msgid "for further information."
 msgstr "w celu dalszych informacji."
 
-#: lib/Chora.php:30
 msgid "hour"
 msgstr "godzina"
 
-#: lib/Chora.php:30
 msgid "hours"
 msgstr "godzin"
 
-#: lib/Chora.php:29
+msgid "long"
+msgstr "d³ugi"
+
 msgid "minute"
 msgstr "minuta"
 
-#: lib/Chora.php:29
 msgid "minutes"
 msgstr "minut"
 
-#: lib/Chora.php:33
 msgid "month"
 msgstr "miesi±c"
 
-#: lib/Chora.php:33
 msgid "months"
 msgstr "miesiêcy"
 
-#: templates/history/branch_cell.inc:6
-#, php-format
+#, c-format
 msgid "revision %s"
 msgstr "wersja %s"
 
-#: lib/Chora.php:28
 msgid "second"
 msgstr "sekunda"
 
-#: lib/Chora.php:28
 msgid "seconds"
 msgstr "sekund"
 
-#: templates/diff/hr/header.inc:13 templates/diff/hr/header.inc:14
-#, php-format
+msgid "unified"
+msgstr "ujednolicony"
+
+#, c-format
 msgid "version %s"
 msgstr "wersja %s"
 
-#: lib/Chora.php:42
 msgid "very little time"
 msgstr "bardzo krótki czas"
 
-#: lib/Chora.php:32
 msgid "week"
 msgstr "tydzieñ"
 
-#: lib/Chora.php:32
 msgid "weeks"
 msgstr "tygodni"
 
-#: lib/Chora.php:34
 msgid "year"
 msgstr "rok"
 
-#: lib/Chora.php:34
 msgid "years"
 msgstr "lat"
diff --git a/po/ro_RO.po b/po/ro_RO.po
index 757275d..f8d526b 100644
--- a/po/ro_RO.po
+++ b/po/ro_RO.po
@@ -1,13 +1,12 @@
 # CHORA ROMANIAN TRANSLATION
-# Copyright (C) 2003 Horde Project
+# Copyright 2003-2009 The Horde Project
 # Eugen Hoanca <eugenh at urban-grafx.ro>, 2003.
 # Marius Dragulescu <mariusd at urban-grafx.ro>, 2003.
 #
 msgid ""
 msgstr ""
 "Project-Id-Version: Chora 1.2\n"
-"Report-Msgid-Bugs-To: dev at lists.horde.org\n"
-"POT-Creation-Date: 2005-10-13 17:16+0200\n"
+"POT-Creation-Date: 2003-02-24 14:30+0200\n"
 "PO-Revision-Date: 2003-01-03 12:00+0200\n"
 "Last-Translator: Eugen Hoanca <eugenh at urban-grafx.ro>\n"
 "Language-Team: Romanian <i18n at lists.horde.org>\n"
@@ -15,11 +14,21 @@ msgstr ""
 "Content-Type: text/plain; charset=ISO-8859-1\n"
 "Content-Transfer-Encoding: 8bit\n"
 
+#: templates/log/request.inc:5
+msgid ""
+" This form allows you to request diffs between any two revisions of a file.  "
+"You may select a symbolic revision name using the selection box or you may "
+"type in a numeric name using the type-in text box."
+msgstr ""
+"Acest formular permite cererea de diferente intre doua revizii a unui fisier."
+"Puteti selecta un nume simbolic de revizie utilizand casuta de selectie sau "
+"scrieti o valoare numerica in casuta de text."
+
 #: templates/annotate/header.inc:4
 msgid "#"
 msgstr "#"
 
-#: templates/patchsets/ps.inc:12 templates/log/rev.inc:26
+#: templates/log/rev.inc:14
 #, php-format
 msgid "%s ago"
 msgstr "acum %s"
@@ -33,12 +42,15 @@ msgstr "Adaugat in v.%s"
 msgid "All Branches"
 msgstr "Toate ramurile"
 
-#: co.php:72 templates/log/rev.inc:8
+#: prefs.php:77
+msgid "An illegal value was specified."
+msgstr "Valoare incorecta specificata."
+
+#: co.php:65 templates/log/rev.inc:8
 msgid "Annotate"
 msgstr "Adnotare"
 
-#: templates/directory/header.inc:18 templates/stats/stats.inc:3
-#: templates/annotate/header.inc:5
+#: templates/annotate/header.inc:5 templates/directory/header.inc:17
 msgid "Author"
 msgstr "Autor"
 
@@ -46,118 +58,101 @@ msgstr "Autor"
 msgid "Back"
 msgstr "Inapoi"
 
-#: templates/checkout/checkout.inc:27 templates/log/rev.inc:31
+#: templates/log/rev.inc:23
 msgid "Branch Point for:"
 msgstr "Ramura pentru:"
 
-#: templates/checkout/checkout.inc:11 templates/patchsets/ps.inc:14
-#: templates/log/rev.inc:28
+#: templates/log/rev.inc:16
 msgid "Branch:"
 msgstr "Ramura:"
 
-#: lib/Chora.php:465
-#, fuzzy
-msgid "Branches"
-msgstr "Toate ramurile"
-
 #: templates/history/branch_cell.inc:2
 msgid "Branching to"
 msgstr "Ramura catre"
 
-#: templates/checkout/checkout.inc:35
+#: annotate.php:26
 #, php-format
-msgid "Changed since <b>%s</b>"
-msgstr "Schimbat din <b>%s><b>"
-
-#: templates/log/rev.inc:37
-#, fuzzy, php-format
-msgid "Changed since <strong>%s</strong>"
-msgstr "Schimbat din <b>%s><b>"
+msgid "CVS Annotation of %s for version %s"
+msgstr "Adnotarea CVS a %s pentru versiunea %s"
 
-#: templates/history/rev.inc:8
+#: history.php:153
 #, php-format
-msgid "Changed: %s"
-msgstr "Schimbat: %s"
+msgid "CVS Branching View for %s"
+msgstr "Vizualizare ramura CVS pentru %s"
 
-#: co.php:70
-#, fuzzy, php-format
-msgid "Checkout of %s (revision %s)"
+#: co.php:63
+#, php-format
+msgid "CVS Checkout of %s (revision %s)"
 msgstr "Verificare CVS a %s (revizie %s)"
 
-#: templates/cvsgraph/cvsgraph.inc:11
-msgid ""
-"Click on the links in between revisions to get a diff between those "
-"revisions."
-msgstr ""
-
-#: templates/cvsgraph/cvsgraph.inc:10
-msgid "Click on the revisions and branches to display the file."
-msgstr ""
+#: cvs.php:35
+#, php-format
+msgid "CVS Directory of /%s"
+msgstr "Director CVS al %s"
 
-#: templates/log/rev.inc:42 templates/log/rev.inc:50
-msgid "Colored"
-msgstr ""
+#: cvs.php:123
+#, php-format
+msgid "CVS Log for %s"
+msgstr "Log CVS pentru %s"
 
-#: templates/checkout/checkout.inc:19 templates/patchsets/ps.inc:17
-#, fuzzy
-msgid "Commit Tags:"
+#: templates/log/rev.inc:19
+msgid "CVS Tags:"
 msgstr "Etichete CVS:"
 
-#: templates/stats/stats.inc:4
-msgid "Commits"
-msgstr ""
+#: templates/log/rev.inc:27
+#, php-format
+msgid "Changed since <b>%s</b>"
+msgstr "Schimbat din <b>%s><b>"
+
+#: templates/history/rev.inc:8
+#, php-format
+msgid "Changed: %s"
+msgstr "Schimbat: %s"
+
+#: templates/headerbar.inc:11
+msgid "Chora Homepage"
+msgstr "Pagina Chora"
+
+#: templates/index/notconfigured.inc:4
+msgid "Chora is not properly configured"
+msgstr "Chora nu este configurat corect"
 
 #: templates/log/request.inc:28
 msgid "Context"
 msgstr "Context"
 
-#: config/prefs.php.dist:11
+#: lib/CVSLib/Checkout.php:43
+msgid "Couldn't perform checkout of the requested file"
+msgstr "Nu se poate verifica fisierul dorit"
+
+#: config/prefs.php.dist:7
 msgid "Customize tasks to run upon logging in to Chora."
 msgstr "Personalizare actiuni la intrarea in Chora."
 
-#: templates/directory/header.inc:23
-#, fuzzy
-msgid "Da_te"
-msgstr "Data"
-
-#: templates/directory/header.inc:23
+#: templates/directory/header.inc:21
 msgid "Date"
 msgstr "Data"
 
-#: patchsets.php:66
-#, fuzzy
-msgid "Deleted"
-msgstr "Fisier sters"
-
-#: templates/directory/file.inc:5 templates/directory/file.inc:17
+#: templates/directory/file.inc:6 templates/directory/file.inc:18
 msgid "Deleted File"
 msgstr "Fisier sters"
 
-#: templates/log/rev.inc:13
-#, fuzzy
-msgid "Deselect"
-msgstr "Deselectat"
-
-#: patchsets.php:63
-#, fuzzy
-msgid "Diff"
-msgstr "Ia Diferente"
-
-#: patchsets.php:46
-msgid "Diff All Files"
-msgstr ""
-
-#: diff.php:69
+#: diff.php:66
 #, php-format
 msgid "Diff for %s between version %s and %s"
 msgstr "Diferenta pentru %s intre versiunile %s si %s"
 
-#: templates/log/rev.inc:49
+#: templates/log/rev.inc:42
 #, php-format
-msgid "Diffs to <strong>%s</strong>"
-msgstr ""
+msgid "Diffs ignoring whitespace to version %s"
+msgstr "Diferentele ignorand spatiile spre versiunea %s"
+
+#: templates/log/rev.inc:48
+#, php-format
+msgid "Diffs to selected version %s"
+msgstr "Diferentele spre versiunea %s"
 
-#: templates/log/rev.inc:41
+#: templates/log/rev.inc:38
 #, php-format
 msgid "Diffs to version %s"
 msgstr "Diferentele la versiunea %s"
@@ -166,7 +161,7 @@ msgstr "Diferentele la versiunea %s"
 msgid "Directory"
 msgstr "Director"
 
-#: annotate.php:30 co.php:73 templates/log/rev.inc:10
+#: annotate.php:29 co.php:66 templates/log/rev.inc:10
 msgid "Download"
 msgstr "Download"
 
@@ -174,7 +169,7 @@ msgstr "Download"
 msgid "Ed Script"
 msgstr "Script editare"
 
-#: templates/error_page.inc:4
+#: status.php:19 templates/error_page.inc:4
 msgid "Error"
 msgstr "Eroare"
 
@@ -182,43 +177,36 @@ msgstr "Eroare"
 msgid "Error Encountered"
 msgstr "Eroare produsa"
 
-#: templates/directory/file.inc:7 templates/directory/file.inc:19
-#: templates/directory/header.inc:8
+#: lib/CVSLib/File.php:218
+msgid "Failed to spawn rlog to retrieve file log information"
+msgstr "Imposibil de recuperat informatiile despre fisierul de log"
+
+#: templates/directory/file.inc:8 templates/directory/file.inc:20
+#: templates/directory/header.inc:9
 msgid "File"
 msgstr "Fisier"
 
-#: templates/patchsets/ps.inc:21
-#, fuzzy
-msgid "Files Changed:"
-msgstr "Schimbat: %s"
-
 #: templates/log/request.inc:44
 msgid "Get Diffs"
 msgstr "Ia Diferente"
 
-#: lib/Chora.php:468
-msgid "Graph"
-msgstr ""
+#: templates/menu/menu.inc:26
+msgid "Help"
+msgstr "Ajutor"
 
-#: cvsgraph.php:48
-#, php-format
-msgid "Graph for %s"
-msgstr ""
-
-#: browse.php:40
+#: cvs.php:39
 msgid "Hide Deleted Files"
 msgstr "Ascunde fisiere sterse"
 
-#: browse.php:40
-#, fuzzy
-msgid "Hide _Deleted Files"
-msgstr "Ascunde fisiere sterse"
-
 #: templates/log/request.inc:26
 msgid "Human Readable"
 msgstr "Normal citibil"
 
-#: templates/directory/header.inc:25
+#: lib/CVSLib/Checkout.php:30
+msgid "Invalid revision number"
+msgstr "Numar de revizie invalid"
+
+#: templates/directory/header.inc:23
 msgid "Last Log"
 msgstr "Ultimul log"
 
@@ -240,50 +228,35 @@ msgstr "Linie"
 msgid "Line %s"
 msgstr "Linia %s"
 
-#: templates/headerbar.inc:11
+#: templates/headerbar.inc:26
 msgid "Location:"
 msgstr "Locatie:"
 
-#: templates/checkout/checkout.inc:42
-msgid "Log:"
-msgstr ""
-
-#: config/prefs.php.dist:10
+#: config/prefs.php.dist:6
 msgid "Login Tasks"
 msgstr "Actiuni la intrare"
 
-#: lib/Chora.php:459
-msgid "Logs"
-msgstr ""
-
-#: templates/log/rev.inc:43 templates/log/rev.inc:51
-#, fuzzy
-msgid "Long"
-msgstr "lung"
-
-#: diff.php:61
-msgid "Malformed Query"
-msgstr ""
-
-#: patchsets.php:59
-#, fuzzy
-msgid "New File"
-msgstr "Fisier"
+#: status.php:32
+msgid "Message"
+msgstr "Mesaj"
 
 #: templates/diff/hr/nochange.inc:6
 msgid "No Viewable Change"
 msgstr "Nici o schimbare vizibila"
 
-#: templates/log/rev.inc:44 templates/log/rev.inc:52
-#, fuzzy
-msgid "NoWhitespaceChanges"
-msgstr "Nici o schimbare vizibila"
+#: lib/CVSLib/File.php:124 lib/CVSLib/File.php:147
+msgid "No revisions"
+msgstr "Nici o revizie"
+
+#: templates/menu/menu.inc:13
+msgid "Options"
+msgstr "Optiuni"
 
-#: config/prefs.php.dist:9
+#: config/prefs.php.dist:5
 msgid "Other Options"
 msgstr "Alte optiuni"
 
-#: lib/Chora.php:368
+#: lib/Chora.php:349
 msgid "Other Repositories"
 msgstr "Alte repozitorii"
 
@@ -291,24 +264,14 @@ msgstr "Alte repozitorii"
 msgid "Parent Directory"
 msgstr "Director parinte"
 
-#: templates/patchsets/ps.inc:6
-#, php-format
-msgid "PatchSet %s</span> by %s"
-msgstr ""
-
-#: lib/Chora.php:462
-msgid "Patchsets"
-msgstr ""
-
-#: patchsets.php:27
-#, php-format
-msgid "Patchsets for %s"
-msgstr ""
-
 #: templates/error_page.inc:13
 msgid "Please contact"
 msgstr "Contact"
 
+#: templates/menu/menu.inc:18
+msgid "Problem?"
+msgstr "Problema?"
+
 #: templates/diff/hr/footer.inc:11
 #, php-format
 msgid "Removed in v.%s"
@@ -318,95 +281,86 @@ msgstr "Eliminat in v.%s"
 msgid "Retrieve diffs between:"
 msgstr "Aducere diferente intre:"
 
-#: templates/directory/header.inc:13 templates/annotate/header.inc:6
+#: templates/annotate/header.inc:6 templates/directory/header.inc:13
 msgid "Rev"
 msgstr "Rev"
 
-#: co.php:77
-#, php-format
-msgid "Revision %s for file %s not found."
-msgstr ""
-
-#: templates/checkout/checkout.inc:4
-#, php-format
-msgid "Revision <b>%s</b>, <i>%s</i> (%s ago) by %s"
-msgstr ""
-
-#: templates/log/rev.inc:15
+#: templates/log/rev.inc:33
 msgid "Select for Diff"
 msgstr "Selectare pentru diferente"
 
-#: browse.php:42
+#: cvs.php:41
 msgid "Show Deleted Files"
 msgstr "Arata fisiere sterse"
 
-#: browse.php:42
-#, fuzzy
-msgid "Show _Deleted Files"
-msgstr "Arata fisiere sterse"
-
 #: templates/log/request.inc:29
 msgid "Side-by-Side"
 msgstr "Alaturat"
 
-#: templates/directory/header.inc:7 templates/directory/header.inc:12
-#: templates/directory/header.inc:17 templates/directory/header.inc:22
+#: templates/index/notconfigured.inc:13
+msgid "Some of Chora's configuration files are missing:"
+msgstr "Anumite fisiere de configurare Chora lipsesc:"
+
+#: templates/directory/header.inc:8 templates/directory/header.inc:12
+#: templates/directory/header.inc:16 templates/directory/header.inc:20
 msgid "Sort Order"
 msgstr "Ordine sortare"
 
-#: annotate.php:27
-#, fuzzy, php-format
-msgid "Source Annotation of %s for version %s"
-msgstr "Adnotarea CVS a %s pentru versiunea %s"
-
-#: history.php:160
-#, fuzzy, php-format
-msgid "Source Branching View for %s"
-msgstr "Vizualizare ramura CVS pentru %s"
+#: status.php:23
+msgid "Success"
+msgstr "Succes"
 
-#: browse.php:32
-#, fuzzy, php-format
-msgid "Source Directory of /%s"
-msgstr "Director CVS al %s"
+#: cvs.php:131
+msgid "Switch to Branch View"
+msgstr "Schimba in vizualizare ramura"
 
-#: browse.php:126
-#, fuzzy, php-format
-msgid "Source Log for %s"
-msgstr "Log CVS pentru %s"
+#: history.php:155
+msgid "Switch to Log View"
+msgstr "Schimba in vizualizare log"
 
-#: lib/Chora.php:182
+#: templates/index/notconfigured.inc:46
 msgid ""
-"SourceRoot not found! This could be a misconfiguration by the server "
-"administrator, or the server could be having temporary problems. Please try "
-"again later."
+"This file controls the default preferences for Chora, and also controls "
+"which preferences users can alter."
 msgstr ""
+"Acest fisier controleaza preferintele implicite precum si alte "
+"preferintepentru Chora."
 
-#: lib/Chora.php:470
-msgid "Statistics"
+#: templates/index/notconfigured.inc:39
+msgid ""
+"This file controls the stylesheet that is used to set colors and fonts in "
+"addition to or overriding Horde defaults."
 msgstr ""
+"Acest fisier determina schema pentru culori si fonturi ce vor inlocui "
+"setarile implicite furnizate de Horde."
 
-#: stats.php:27
-#, php-format
-msgid "Statistics for %s"
+#: templates/index/notconfigured.inc:25
+msgid ""
+"This file defines all of the cvs repositories that you wish Chora to display."
 msgstr ""
+"Acest fisier defineste toate repozitoriile cvs pe care Chora le va afisa."
 
-#: templates/log/rev.inc:34
-#, fuzzy
-msgid "Tags:"
-msgstr "Etichete CVS:"
+#: templates/index/notconfigured.inc:32
+msgid "This file defines any special MIME-type handling for Chora."
+msgstr "Acest fisier defineste tipurile MIME pentru Chora."
 
-#: templates/log/request.inc:5
-#, fuzzy
+#: templates/index/notconfigured.inc:18
 msgid ""
-"This form allows you to request diffs between any two revisions of a file.  "
-"You may select a symbolic revision name using the selection box or you may "
-"type in a numeric name using the type-in text box."
+"This is the main Chora configuration file. It contains paths and options for "
+"all Chora scripts."
 msgstr ""
-"Acest formular permite cererea de diferente intre doua revizii a unui fisier."
-"Puteti selecta un nume simbolic de revizie utilizand casuta de selectie sau "
-"scrieti o valoare numerica in casuta de text."
+"Acesta e fisierul principal de configurare Chora. Contine caile si optiunile "
+"pentru scripturile Chora."
 
-#: templates/headerbar.inc:16
+#: prefs.php:86
+msgid "This number must be at least one."
+msgstr "Numarul trebuie sa fie minim 1."
+
+#: prefs.php:84
+msgid "This value must be a number."
+msgstr "Valoarea trebuie sa fie numerica."
+
+#: templates/headerbar.inc:32
 msgid "Tracking Branch"
 msgstr "Ramura de cautare"
 
@@ -418,15 +372,23 @@ msgstr "Tip:"
 msgid "Unified"
 msgstr "Unificat"
 
+#: templates/log/rev.inc:31
+msgid "Unselect"
+msgstr "Deselectat"
+
 #: templates/log/request.inc:18 templates/log/request.inc:38
 msgid "Use Text Field"
 msgstr "Utilzare camp text"
 
-#: config/prefs.php.dist:37
+#: config/prefs.php.dist:33
 msgid "Use last viewed file or directory at login time"
 msgstr "Utilizare ultima vizualizare de fisier sau director la intrare"
 
-#: annotate.php:29 templates/log/rev.inc:9
+#: prefs.php:133
+msgid "User Options"
+msgstr "Optiuni utilizator"
+
+#: annotate.php:28 templates/log/rev.inc:9
 msgid "View"
 msgstr "Vizualizare"
 
@@ -438,56 +400,19 @@ msgstr "Vizualizeaza Ramura"
 msgid "View revisions on:"
 msgstr "Vizualizeaza revizii pe:"
 
-#: lib/Chora.php:472
-#, fuzzy
-msgid "View:"
-msgstr "Vizualizare"
-
-#: templates/directory/header.inc:18
-#, fuzzy
-msgid "_Author"
-msgstr "Autor"
+#: status.php:27
+msgid "Warning"
+msgstr "Avertisment"
 
-#: lib/Chora.php:465
-#, fuzzy
-msgid "_Branches"
-msgstr "Toate ramurile"
-
-#: lib/Chora.php:439
-msgid "_Browse"
-msgstr ""
-
-#: templates/directory/header.inc:8
-#, fuzzy
-msgid "_File"
-msgstr "Fisier"
-
-#: lib/Chora.php:468
-msgid "_Graph"
-msgstr ""
-
-#: lib/Chora.php:459
-msgid "_Logs"
-msgstr ""
-
-#: lib/Chora.php:462
-msgid "_Patchsets"
-msgstr ""
-
-#: templates/directory/header.inc:13
-#, fuzzy
-msgid "_Rev"
-msgstr "Rev"
-
-#: lib/Chora.php:470
-msgid "_Statistics"
-msgstr ""
+#: prefs.php:118
+msgid "Your options have been updated."
+msgstr "Optiunile au fost salvate."
 
 #: templates/log/request.inc:35
 msgid "and:"
 msgstr "si:"
 
-#: templates/history/rev.inc:4 templates/log/rev.inc:5
+#: templates/log/rev.inc:6 templates/history/rev.inc:4
 #, php-format
 msgid "by %s"
 msgstr "de catre %s"
@@ -496,11 +421,11 @@ msgstr "de catre %s"
 msgid "changed lines"
 msgstr "linii schimbate"
 
-#: lib/Chora.php:31
+#: lib/CVSLib/File.php:292
 msgid "day"
 msgstr "zi"
 
-#: lib/Chora.php:31
+#: lib/CVSLib/File.php:292
 msgid "days"
 msgstr "zile"
 
@@ -508,27 +433,31 @@ msgstr "zile"
 msgid "for further information."
 msgstr "pentru informatii suplimentare."
 
-#: lib/Chora.php:30
+#: lib/CVSLib/File.php:291
 msgid "hour"
 msgstr "ora"
 
-#: lib/Chora.php:30
+#: lib/CVSLib/File.php:291
 msgid "hours"
 msgstr "ore"
 
-#: lib/Chora.php:29
+#: templates/log/rev.inc:39 templates/log/rev.inc:43 templates/log/rev.inc:49
+msgid "long"
+msgstr "lung"
+
+#: lib/CVSLib/File.php:290
 msgid "minute"
 msgstr "minut"
 
-#: lib/Chora.php:29
+#: lib/CVSLib/File.php:290
 msgid "minutes"
 msgstr "minute"
 
-#: lib/Chora.php:33
+#: lib/CVSLib/File.php:294
 msgid "month"
 msgstr "luna"
 
-#: lib/Chora.php:33
+#: lib/CVSLib/File.php:294
 msgid "months"
 msgstr "luni"
 
@@ -537,35 +466,39 @@ msgstr "luni"
 msgid "revision %s"
 msgstr "revizia %s"
 
-#: lib/Chora.php:28
+#: lib/CVSLib/File.php:289
 msgid "second"
 msgstr "secunda"
 
-#: lib/Chora.php:28
+#: lib/CVSLib/File.php:289
 msgid "seconds"
 msgstr "secunde"
 
+#: templates/log/rev.inc:40 templates/log/rev.inc:44 templates/log/rev.inc:50
+msgid "unified"
+msgstr "unificat"
+
 #: templates/diff/hr/header.inc:13 templates/diff/hr/header.inc:14
 #, php-format
 msgid "version %s"
 msgstr "versiune %s"
 
-#: lib/Chora.php:42
+#: lib/CVSLib/File.php:303
 msgid "very little time"
 msgstr "foarte putin timp"
 
-#: lib/Chora.php:32
+#: lib/CVSLib/File.php:293
 msgid "week"
 msgstr "saptamana"
 
-#: lib/Chora.php:32
+#: lib/CVSLib/File.php:293
 msgid "weeks"
 msgstr "saptamani"
 
-#: lib/Chora.php:34
+#: lib/CVSLib/File.php:295
 msgid "year"
 msgstr "an"
 
-#: lib/Chora.php:34
+#: lib/CVSLib/File.php:295
 msgid "years"
 msgstr "ani"
diff --git a/po/ru_RU.po b/po/ru_RU.po
index 3f4c573..6401052 100644
--- a/po/ru_RU.po
+++ b/po/ru_RU.po
@@ -1,11 +1,11 @@
 # Chora Russian translation.
-# Copyright (C) 2005 Jan Schneider.
+# Copyright 2005 Jan Schneider.
 # Illya Belov <belov at iop.irkps.ru>, 2005.
 msgid ""
 msgstr ""
 "Project-Id-Version: Chora 2.1-cvs\n"
 "Report-Msgid-Bugs-To: dev at lists.horde.org\n"
-"POT-Creation-Date: 2005-10-13 17:16+0200\n"
+"POT-Creation-Date: 2005-07-27 02:04+0200\n"
 "PO-Revision-Date: 2005-07-27 02:40+0200\n"
 "Last-Translator: Illya Belov <jan at horde.org>\n"
 "Language-Team: i18n at lists.horde.org\n"
@@ -35,7 +35,7 @@ msgstr "
 msgid "Annotate"
 msgstr "Àííîòèðîâàòü"
 
-#: templates/directory/header.inc:18 templates/stats/stats.inc:3
+#: templates/directory/header.inc:16 templates/stats/stats.inc:3
 #: templates/annotate/header.inc:5
 msgid "Author"
 msgstr "Àâòîð"
@@ -53,7 +53,7 @@ msgstr "
 msgid "Branch:"
 msgstr "Âåòâü:"
 
-#: lib/Chora.php:465
+#: lib/Chora.php:464
 msgid "Branches"
 msgstr "Âåòâè"
 
@@ -61,12 +61,7 @@ msgstr "
 msgid "Branching to"
 msgstr "Branching nach"
 
-#: templates/checkout/checkout.inc:35
-#, fuzzy, php-format
-msgid "Changed since <b>%s</b>"
-msgstr "Èçìåíåíî <strong>%s</strong>"
-
-#: templates/log/rev.inc:37
+#: templates/checkout/checkout.inc:35 templates/log/rev.inc:37
 #, php-format
 msgid "Changed since <strong>%s</strong>"
 msgstr "Èçìåíåíî <strong>%s</strong>"
@@ -111,11 +106,11 @@ msgstr "Context"
 msgid "Customize tasks to run upon logging in to Chora."
 msgstr "Íàñòðîéêà çàäà÷ äëÿ çàïóñêà logging â Chora."
 
-#: templates/directory/header.inc:23
+#: templates/directory/header.inc:21
 msgid "Da_te"
 msgstr "Äàòà"
 
-#: templates/directory/header.inc:23
+#: templates/directory/header.inc:21
 msgid "Date"
 msgstr "Äàòà"
 
@@ -175,7 +170,7 @@ msgid "Error Encountered"
 msgstr "Îøèáêè âñòðå÷àþòñÿ"
 
 #: templates/directory/file.inc:7 templates/directory/file.inc:19
-#: templates/directory/header.inc:8
+#: templates/directory/header.inc:6
 msgid "File"
 msgstr "Ôàéë"
 
@@ -187,7 +182,7 @@ msgstr "
 msgid "Get Diffs"
 msgstr "Ïðîñìîòð ðàçëè÷èé"
 
-#: lib/Chora.php:468
+#: lib/Chora.php:467
 msgid "Graph"
 msgstr "Ãðàôèê"
 
@@ -208,7 +203,7 @@ msgstr "
 msgid "Human Readable"
 msgstr "Human Readable"
 
-#: templates/directory/header.inc:25
+#: templates/directory/header.inc:23
 msgid "Last Log"
 msgstr "Ïîñëåäíåå ñîîáùåíèå"
 
@@ -242,7 +237,7 @@ msgstr "Log:"
 msgid "Login Tasks"
 msgstr "Çàäà÷è ïðè âõîäå:"
 
-#: lib/Chora.php:459
+#: lib/Chora.php:458
 msgid "Logs"
 msgstr "Èñòîðèÿ"
 
@@ -283,7 +278,7 @@ msgstr "
 msgid "PatchSet %s</span> by %s"
 msgstr "PatcSet %s</span> îò %s"
 
-#: lib/Chora.php:462
+#: lib/Chora.php:461
 msgid "Patchsets"
 msgstr "Patchsets"
 
@@ -305,7 +300,7 @@ msgstr "
 msgid "Retrieve diffs between:"
 msgstr "Íàéäåííûå ðàçëè÷èÿ ìåæäó:"
 
-#: templates/directory/header.inc:13 templates/annotate/header.inc:6
+#: templates/directory/header.inc:11 templates/annotate/header.inc:6
 msgid "Rev"
 msgstr "Ðåâ."
 
@@ -315,8 +310,8 @@ msgid "Revision %s for file %s not found."
 msgstr "Ðåâèçèÿ %s äëÿ ôàéëà %s íå íàéäåíà."
 
 #: templates/checkout/checkout.inc:4
-#, fuzzy, php-format
-msgid "Revision <b>%s</b>, <i>%s</i> (%s ago) by %s"
+#, php-format
+msgid "Revision <strong>%s</strong>, <em>%s</em> (%s ago) by %s"
 msgstr "Ðåâèçèÿ <strong>%s</strong>, <em>%s</em> (%s) îò %s"
 
 #: templates/log/rev.inc:15
@@ -335,8 +330,8 @@ msgstr "
 msgid "Side-by-Side"
 msgstr "Ñòðîêà-ê-ñòðîêå"
 
-#: templates/directory/header.inc:7 templates/directory/header.inc:12
-#: templates/directory/header.inc:17 templates/directory/header.inc:22
+#: templates/directory/header.inc:5 templates/directory/header.inc:10
+#: templates/directory/header.inc:15 templates/directory/header.inc:20
 msgid "Sort Order"
 msgstr "Ñîðòèðîâêà"
 
@@ -369,7 +364,7 @@ msgstr ""
 "SourceRoot íå íàéäåíà! Âîçìîæíî ýòî íàñòðîéêà ñåðâåðà èëè íà ñåðâåðå "
 "âðåìåííûå ïðîáëåìû. Ïîæàëóéñòà, çàéäèòå ïîçæå."
 
-#: lib/Chora.php:470
+#: lib/Chora.php:469
 msgid "Statistics"
 msgstr "Ñòàòèñòèêà"
 
@@ -424,43 +419,43 @@ msgstr "
 msgid "View revisions on:"
 msgstr "Ïðîñìîòð ðèâèçèé íà:"
 
-#: lib/Chora.php:472
+#: lib/Chora.php:471
 msgid "View:"
 msgstr "Ïðîñìîòð:"
 
-#: templates/directory/header.inc:18
+#: templates/directory/header.inc:16
 msgid "_Author"
 msgstr "Àâòîð"
 
-#: lib/Chora.php:465
+#: lib/Chora.php:464
 msgid "_Branches"
 msgstr "Âåòâè"
 
-#: lib/Chora.php:439
+#: lib/Chora.php:438
 msgid "_Browse"
 msgstr "Ïðîñìîòð"
 
-#: templates/directory/header.inc:8
+#: templates/directory/header.inc:6
 msgid "_File"
 msgstr "Ôàéë"
 
-#: lib/Chora.php:468
+#: lib/Chora.php:467
 msgid "_Graph"
 msgstr "Ãðàôèê"
 
-#: lib/Chora.php:459
+#: lib/Chora.php:458
 msgid "_Logs"
 msgstr "_Logs"
 
-#: lib/Chora.php:462
+#: lib/Chora.php:461
 msgid "_Patchsets"
 msgstr "_Patchsets"
 
-#: templates/directory/header.inc:13
+#: templates/directory/header.inc:11
 msgid "_Rev"
 msgstr "Ðåâ."
 
-#: lib/Chora.php:470
+#: lib/Chora.php:469
 msgid "_Statistics"
 msgstr "Ñòàòèñòèêà"
 
diff --git a/po/sk_SK.po b/po/sk_SK.po
index e0642a1..565a63a 100644
--- a/po/sk_SK.po
+++ b/po/sk_SK.po
@@ -1,15 +1,15 @@
 # Slovak translations for horde package
 # Slovenské preklady pre balík horde.
-# Copyright (C) 2004 Horde Project
-# (C) 2004, 2005 Ivan Noris <vix at vazka.sk>
+# Copyright 2004-2009 The Horde Project
+# (C) 2004 Ivan Noris <vix at vazka.sk>
 # This file is distributed under the same license as the horde package.
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: Chora 2.0.1\n"
+"Project-Id-Version: Chora 2.0\n"
 "Report-Msgid-Bugs-To: dev at lists.horde.org\n"
-"POT-Creation-Date: 2005-10-13 17:16+0200\n"
-"PO-Revision-Date: 2005-09-01 20:05+0100\n"
+"POT-Creation-Date: 2004-12-11 19:53+0100\n"
+"PO-Revision-Date: 2004-12-11 19:53+0100\n"
 "Last-Translator: Ivan Noris <vix at vazka.sk>\n"
 "Language-Team: Slovak <vix at vazka.sk>\n"
 "MIME-Version: 1.0\n"
@@ -21,7 +21,7 @@ msgstr ""
 msgid "#"
 msgstr "È."
 
-#: templates/patchsets/ps.inc:12 templates/log/rev.inc:26
+#: templates/patchsets/ps.inc:12 templates/log/rev.inc:24
 #, php-format
 msgid "%s ago"
 msgstr "pred %s"
@@ -35,7 +35,7 @@ msgstr "Pridan
 msgid "All Branches"
 msgstr "V¹etky vetvy"
 
-#: co.php:72 templates/log/rev.inc:8
+#: co.php:68 templates/log/rev.inc:8
 msgid "Annotate"
 msgstr "Komentova»"
 
@@ -48,16 +48,16 @@ msgstr "Autor"
 msgid "Back"
 msgstr "Naspä»"
 
-#: templates/checkout/checkout.inc:27 templates/log/rev.inc:31
+#: templates/checkout/checkout.inc:27 templates/log/rev.inc:29
 msgid "Branch Point for:"
 msgstr "Vetviaci bod pre:"
 
 #: templates/checkout/checkout.inc:11 templates/patchsets/ps.inc:14
-#: templates/log/rev.inc:28
+#: templates/log/rev.inc:26
 msgid "Branch:"
 msgstr "Vetva:"
 
-#: lib/Chora.php:465
+#: lib/Chora.php:467
 msgid "Branches"
 msgstr "Vetvy"
 
@@ -65,22 +65,17 @@ msgstr "Vetvy"
 msgid "Branching to"
 msgstr "Vetviaci sa do"
 
-#: templates/checkout/checkout.inc:35
+#: templates/checkout/checkout.inc:35 templates/log/rev.inc:35
 #, php-format
 msgid "Changed since <b>%s</b>"
 msgstr "Zmenené od <b>%s</b>"
 
-#: templates/log/rev.inc:37
-#, php-format
-msgid "Changed since <strong>%s</strong>"
-msgstr "Zmenené od <strong>%s</strong>"
-
 #: templates/history/rev.inc:8
 #, php-format
 msgid "Changed: %s"
 msgstr "Zmenené: %s"
 
-#: co.php:70
+#: co.php:66
 #, php-format
 msgid "Checkout of %s (revision %s)"
 msgstr "Výber %s (revízia %s)"
@@ -95,7 +90,7 @@ msgstr "Kliknite na linky medzi rev
 msgid "Click on the revisions and branches to display the file."
 msgstr "Kliknite na revízie a vetvy pre zobrazenie súboru."
 
-#: templates/log/rev.inc:42 templates/log/rev.inc:50
+#: templates/log/rev.inc:40 templates/log/rev.inc:49
 msgid "Colored"
 msgstr "Farebné"
 
@@ -128,7 +123,7 @@ msgstr "D
 msgid "Deleted"
 msgstr "Vymazané"
 
-#: templates/directory/file.inc:5 templates/directory/file.inc:17
+#: templates/directory/file.inc:6 templates/directory/file.inc:18
 msgid "Deleted File"
 msgstr "Vymazaný súbor"
 
@@ -149,12 +144,12 @@ msgstr "Rozdiely medzi v
 msgid "Diff for %s between version %s and %s"
 msgstr "Rozdiely pre %s medzi verziami %s a %s"
 
-#: templates/log/rev.inc:49
+#: templates/log/rev.inc:48
 #, php-format
-msgid "Diffs to <strong>%s</strong>"
-msgstr "Rozdiely voèi <strong>%s</strong>"
+msgid "Diffs to <b>%s</b>"
+msgstr "Rozdiely voèi <b>%s</b>"
 
-#: templates/log/rev.inc:41
+#: templates/log/rev.inc:39
 #, php-format
 msgid "Diffs to version %s"
 msgstr "Rozdiely voèi verzii %s"
@@ -163,7 +158,7 @@ msgstr "Rozdiely vo
 msgid "Directory"
 msgstr "Adresár"
 
-#: annotate.php:30 co.php:73 templates/log/rev.inc:10
+#: annotate.php:30 co.php:69 templates/log/rev.inc:10
 msgid "Download"
 msgstr "Stiahnu»"
 
@@ -179,7 +174,7 @@ msgstr "Chyba"
 msgid "Error Encountered"
 msgstr "Nastala chyba"
 
-#: templates/directory/file.inc:7 templates/directory/file.inc:19
+#: templates/directory/file.inc:8 templates/directory/file.inc:20
 #: templates/directory/header.inc:8
 msgid "File"
 msgstr "Súbor"
@@ -192,7 +187,7 @@ msgstr "Zmenen
 msgid "Get Diffs"
 msgstr "Získa» rozdiely"
 
-#: lib/Chora.php:468
+#: lib/Chora.php:470
 msgid "Graph"
 msgstr "Graf"
 
@@ -247,11 +242,11 @@ msgstr "Z
 msgid "Login Tasks"
 msgstr "Úlohy pri prihlásení"
 
-#: lib/Chora.php:459
+#: lib/Chora.php:461
 msgid "Logs"
 msgstr "Záznamy"
 
-#: templates/log/rev.inc:43 templates/log/rev.inc:51
+#: templates/log/rev.inc:41 templates/log/rev.inc:50
 msgid "Long"
 msgstr "Podrobnosti"
 
@@ -267,7 +262,7 @@ msgstr "Nov
 msgid "No Viewable Change"
 msgstr "®iadne zobraziteµné zmeny"
 
-#: templates/log/rev.inc:44 templates/log/rev.inc:52
+#: templates/log/rev.inc:42 templates/log/rev.inc:51
 msgid "NoWhitespaceChanges"
 msgstr "®iadne zmeny medzier alebo oddeµovacích znakov"
 
@@ -275,7 +270,7 @@ msgstr "
 msgid "Other Options"
 msgstr "Iné nastavenia"
 
-#: lib/Chora.php:368
+#: lib/Chora.php:369
 msgid "Other Repositories"
 msgstr "Iné úlo¾iská"
 
@@ -288,7 +283,7 @@ msgstr "Rodi
 msgid "PatchSet %s</span> by %s"
 msgstr "PatchSet %s</span> vytvoril(a) %s"
 
-#: lib/Chora.php:462
+#: lib/Chora.php:464
 msgid "Patchsets"
 msgstr "Patchsets"
 
@@ -314,7 +309,7 @@ msgstr "Z
 msgid "Rev"
 msgstr "Rev"
 
-#: co.php:77
+#: co.php:73
 #, php-format
 msgid "Revision %s for file %s not found."
 msgstr "Revízia %s pre súbor %s nebola nájdená."
@@ -351,9 +346,9 @@ msgid "Source Annotation of %s for version %s"
 msgstr "Komentár k zdroju %s pre verziu %s"
 
 #: history.php:160
-#, fuzzy, php-format
+#, php-format
 msgid "Source Branching View for %s"
-msgstr "Zdrojový záznam pre %s"
+msgstr ""
 
 #: browse.php:32
 #, php-format
@@ -365,7 +360,7 @@ msgstr "Zdrojov
 msgid "Source Log for %s"
 msgstr "Zdrojový záznam pre %s"
 
-#: lib/Chora.php:182
+#: lib/Chora.php:183
 msgid ""
 "SourceRoot not found! This could be a misconfiguration by the server "
 "administrator, or the server could be having temporary problems. Please try "
@@ -374,7 +369,7 @@ msgstr ""
 "SourceRoot nebol nájdený! Toto mô¾e by» spôsobené nesprávnou konfiguráciou, "
 "alebo má server doèasné problémy. Prosím, skúste to znovu neskôr."
 
-#: lib/Chora.php:470
+#: lib/Chora.php:472
 msgid "Statistics"
 msgstr "©tatistiky"
 
@@ -383,7 +378,7 @@ msgstr "
 msgid "Statistics for %s"
 msgstr "©tatistiky pre %s"
 
-#: templates/log/rev.inc:34
+#: templates/log/rev.inc:32
 msgid "Tags:"
 msgstr "Znaèky:"
 
@@ -399,7 +394,7 @@ msgstr ""
 
 #: templates/headerbar.inc:16
 msgid "Tracking Branch"
-msgstr "Sledovanie vetvy"
+msgstr ""
 
 #: templates/log/request.inc:23
 msgid "Type:"
@@ -429,7 +424,7 @@ msgstr "Zobrazi
 msgid "View revisions on:"
 msgstr "Zobrazi» revízie pre:"
 
-#: lib/Chora.php:472
+#: lib/Chora.php:474
 msgid "View:"
 msgstr "Zobrazi»:"
 
@@ -437,11 +432,11 @@ msgstr "Zobrazi
 msgid "_Author"
 msgstr "_Autor"
 
-#: lib/Chora.php:465
+#: lib/Chora.php:467
 msgid "_Branches"
 msgstr "_Vetvy"
 
-#: lib/Chora.php:439
+#: lib/Chora.php:441
 msgid "_Browse"
 msgstr "_Prezera»"
 
@@ -449,15 +444,15 @@ msgstr "_Prezera
 msgid "_File"
 msgstr "_Súbor"
 
-#: lib/Chora.php:468
+#: lib/Chora.php:470
 msgid "_Graph"
 msgstr "_Graf"
 
-#: lib/Chora.php:459
+#: lib/Chora.php:461
 msgid "_Logs"
 msgstr "_Záznamy"
 
-#: lib/Chora.php:462
+#: lib/Chora.php:464
 msgid "_Patchsets"
 msgstr "_Patchsets"
 
@@ -465,7 +460,7 @@ msgstr "_Patchsets"
 msgid "_Rev"
 msgstr "_Rev"
 
-#: lib/Chora.php:470
+#: lib/Chora.php:472
 msgid "_Statistics"
 msgstr "©tati_stiky"
 
@@ -482,11 +477,11 @@ msgstr "vytvoren
 msgid "changed lines"
 msgstr "zmenené riadky"
 
-#: lib/Chora.php:31
+#: lib/Chora.php:32
 msgid "day"
 msgstr "deò"
 
-#: lib/Chora.php:31
+#: lib/Chora.php:32
 msgid "days"
 msgstr "dni"
 
@@ -494,27 +489,27 @@ msgstr "dni"
 msgid "for further information."
 msgstr "pre ïal¹ie informácie."
 
-#: lib/Chora.php:30
+#: lib/Chora.php:31
 msgid "hour"
 msgstr "hodina"
 
-#: lib/Chora.php:30
+#: lib/Chora.php:31
 msgid "hours"
 msgstr "hodín"
 
-#: lib/Chora.php:29
+#: lib/Chora.php:30
 msgid "minute"
 msgstr "minúta"
 
-#: lib/Chora.php:29
+#: lib/Chora.php:30
 msgid "minutes"
 msgstr "minút"
 
-#: lib/Chora.php:33
+#: lib/Chora.php:34
 msgid "month"
 msgstr "mesiac"
 
-#: lib/Chora.php:33
+#: lib/Chora.php:34
 msgid "months"
 msgstr "mesiacov"
 
@@ -523,11 +518,11 @@ msgstr "mesiacov"
 msgid "revision %s"
 msgstr "revízia %s"
 
-#: lib/Chora.php:28
+#: lib/Chora.php:29
 msgid "second"
 msgstr "sekunda"
 
-#: lib/Chora.php:28
+#: lib/Chora.php:29
 msgid "seconds"
 msgstr "sekúnd"
 
@@ -536,22 +531,22 @@ msgstr "sek
 msgid "version %s"
 msgstr "verzia %s"
 
-#: lib/Chora.php:42
+#: lib/Chora.php:43
 msgid "very little time"
 msgstr "veµmi krátky èas"
 
-#: lib/Chora.php:32
+#: lib/Chora.php:33
 msgid "week"
 msgstr "tý¾deò"
 
-#: lib/Chora.php:32
+#: lib/Chora.php:33
 msgid "weeks"
 msgstr "tý¾dòov"
 
-#: lib/Chora.php:34
+#: lib/Chora.php:35
 msgid "year"
 msgstr "rok"
 
-#: lib/Chora.php:34
+#: lib/Chora.php:35
 msgid "years"
 msgstr "rokov"
diff --git a/po/sv_SE.po b/po/sv_SE.po
index f8e6e5f..812ac0c 100644
--- a/po/sv_SE.po
+++ b/po/sv_SE.po
@@ -1,13 +1,13 @@
 # Chora Swedish translation
-# Copyright (C) 2002-2004 Andreas Dahlén.
-# Andreas Dahlén <andreas at dahlen.ws>, 2004.
+# Copyright 2002-2005 Andreas Dahlén.
+# Andreas Dahlén <andreas at dahlen.ws>, 2005.
 #
 msgid ""
 msgstr ""
 "Project-Id-Version: Chora 2.0-cvs\n"
 "Report-Msgid-Bugs-To: dev at lists.horde.org\n"
-"POT-Creation-Date: 2005-10-13 17:16+0200\n"
-"PO-Revision-Date: 2004-12-05 13:32+0100\n"
+"POT-Creation-Date: 2005-06-14 22:08+0200\n"
+"PO-Revision-Date: 2005-06-16 13:32+0100\n"
 "Last-Translator: Andreas Dahlén <andreas at dahlen.ws>\n"
 "Language-Team: Swedish <dev at lists.horde.org>\n"
 "MIME-Version: 1.0\n"
@@ -18,7 +18,7 @@ msgstr ""
 msgid "#"
 msgstr "#"
 
-#: templates/patchsets/ps.inc:12 templates/log/rev.inc:26
+#: templates/log/rev.inc:24 templates/patchsets/ps.inc:12
 #, php-format
 msgid "%s ago"
 msgstr "för %s sedan"
@@ -36,7 +36,7 @@ msgstr "Alla grenar"
 msgid "Annotate"
 msgstr "Kommentera"
 
-#: templates/directory/header.inc:18 templates/stats/stats.inc:3
+#: templates/directory/header.inc:16 templates/stats/stats.inc:3
 #: templates/annotate/header.inc:5
 msgid "Author"
 msgstr "Författare"
@@ -45,16 +45,16 @@ msgstr "F
 msgid "Back"
 msgstr "Tillbaka"
 
-#: templates/checkout/checkout.inc:27 templates/log/rev.inc:31
+#: templates/log/rev.inc:29 templates/checkout/checkout.inc:27
 msgid "Branch Point for:"
 msgstr "Grenpunkt för:"
 
-#: templates/checkout/checkout.inc:11 templates/patchsets/ps.inc:14
-#: templates/log/rev.inc:28
+#: templates/log/rev.inc:26 templates/checkout/checkout.inc:11
+#: templates/patchsets/ps.inc:14
 msgid "Branch:"
 msgstr "Gren:"
 
-#: lib/Chora.php:465
+#: lib/Chora.php:466
 msgid "Branches"
 msgstr "Grenar"
 
@@ -62,15 +62,10 @@ msgstr "Grenar"
 msgid "Branching to"
 msgstr "Förgrenar till"
 
-#: templates/checkout/checkout.inc:35
+#: templates/log/rev.inc:35 templates/checkout/checkout.inc:35
 #, php-format
-msgid "Changed since <b>%s</b>"
-msgstr "Ädrad sedan <b>%s</b>"
-
-#: templates/log/rev.inc:37
-#, fuzzy, php-format
 msgid "Changed since <strong>%s</strong>"
-msgstr "Ädrad sedan <b>%s</b>"
+msgstr "Ädrad sedan <strong>%s</strong>"
 
 #: templates/history/rev.inc:8
 #, php-format
@@ -94,7 +89,7 @@ msgstr ""
 msgid "Click on the revisions and branches to display the file."
 msgstr "Klicka på revisionen och gren för att visa filen."
 
-#: templates/log/rev.inc:42 templates/log/rev.inc:50
+#: templates/log/rev.inc:40 templates/log/rev.inc:49
 msgid "Colored"
 msgstr "Färgad"
 
@@ -114,11 +109,11 @@ msgstr "Kontext"
 msgid "Customize tasks to run upon logging in to Chora."
 msgstr "Skräddarsy aktiviteter som körs vid inloggning till Chora."
 
-#: templates/directory/header.inc:23
+#: templates/directory/header.inc:21
 msgid "Da_te"
 msgstr "Da_tum"
 
-#: templates/directory/header.inc:23
+#: templates/directory/header.inc:21
 msgid "Date"
 msgstr "Datum"
 
@@ -147,23 +142,23 @@ msgstr "J
 msgid "Diff for %s between version %s and %s"
 msgstr "Skillnader för %s mellan version %s och %s"
 
-#: templates/log/rev.inc:49
-#, fuzzy, php-format
+#: templates/log/rev.inc:48
+#, php-format
 msgid "Diffs to <strong>%s</strong>"
-msgstr "Skillnader till <b>%s</b>"
+msgstr "Skillnader till <strong>%s</strong>"
 
-#: templates/log/rev.inc:41
+#: templates/log/rev.inc:39
 #, php-format
 msgid "Diffs to version %s"
 msgstr "Skillnad mot version %s"
 
 #: templates/directory/dir.inc:5 templates/directory/dir.inc:7
 msgid "Directory"
-msgstr "Mapp"
+msgstr "Katalog"
 
 #: annotate.php:30 co.php:73 templates/log/rev.inc:10
 msgid "Download"
-msgstr "Ladda ner"
+msgstr "Hämta"
 
 #: templates/log/request.inc:30
 msgid "Ed Script"
@@ -178,7 +173,7 @@ msgid "Error Encountered"
 msgstr "Fel uppstod"
 
 #: templates/directory/file.inc:7 templates/directory/file.inc:19
-#: templates/directory/header.inc:8
+#: templates/directory/header.inc:6
 msgid "File"
 msgstr "Fil"
 
@@ -190,7 +185,7 @@ msgstr "
 msgid "Get Diffs"
 msgstr "Hämta skillnader"
 
-#: lib/Chora.php:468
+#: lib/Chora.php:469
 msgid "Graph"
 msgstr "Graf"
 
@@ -211,7 +206,7 @@ msgstr "G
 msgid "Human Readable"
 msgstr "Läsbar för människor"
 
-#: templates/directory/header.inc:25
+#: templates/directory/header.inc:23
 msgid "Last Log"
 msgstr "Senaste loggmeddelande"
 
@@ -245,11 +240,11 @@ msgstr "Logg:"
 msgid "Login Tasks"
 msgstr "Loginaktiviteter"
 
-#: lib/Chora.php:459
+#: lib/Chora.php:460
 msgid "Logs"
 msgstr "Loggar"
 
-#: templates/log/rev.inc:43 templates/log/rev.inc:51
+#: templates/log/rev.inc:41 templates/log/rev.inc:50
 msgid "Long"
 msgstr "Lång"
 
@@ -265,7 +260,7 @@ msgstr "Ny fil"
 msgid "No Viewable Change"
 msgstr "Inga synbara skillnader"
 
-#: templates/log/rev.inc:44 templates/log/rev.inc:52
+#: templates/log/rev.inc:42 templates/log/rev.inc:51
 msgid "NoWhitespaceChanges"
 msgstr "Inga whitespace skillnader"
 
@@ -286,7 +281,7 @@ msgstr "Ovanst
 msgid "PatchSet %s</span> by %s"
 msgstr "Patchset %s</span> av %s"
 
-#: lib/Chora.php:462
+#: lib/Chora.php:463
 msgid "Patchsets"
 msgstr "Patchsets"
 
@@ -308,7 +303,7 @@ msgstr "Borttagen i v.%s"
 msgid "Retrieve diffs between:"
 msgstr "Hämta skillnader mellan:"
 
-#: templates/directory/header.inc:13 templates/annotate/header.inc:6
+#: templates/directory/header.inc:11 templates/annotate/header.inc:6
 msgid "Rev"
 msgstr "Rev"
 
@@ -319,8 +314,8 @@ msgstr "Revision %s f
 
 #: templates/checkout/checkout.inc:4
 #, php-format
-msgid "Revision <b>%s</b>, <i>%s</i> (%s ago) by %s"
-msgstr "Revision <b>%s</b>, <i>%s</i> (%s sedan) av %s"
+msgid "Revision <strong>%s</strong>, <em>%s</em> (%s ago) by %s"
+msgstr "Revision <strong>%s</strong>, <em>%s</em> (sedan %s) av %s"
 
 #: templates/log/rev.inc:15
 msgid "Select for Diff"
@@ -338,8 +333,8 @@ msgstr "Visa _raderade filer"
 msgid "Side-by-Side"
 msgstr "Sida vid sida"
 
-#: templates/directory/header.inc:7 templates/directory/header.inc:12
-#: templates/directory/header.inc:17 templates/directory/header.inc:22
+#: templates/directory/header.inc:5 templates/directory/header.inc:10
+#: templates/directory/header.inc:15 templates/directory/header.inc:20
 msgid "Sort Order"
 msgstr "Sorteringsordning"
 
@@ -372,7 +367,7 @@ msgstr ""
 "SourceRoot saknas! Det kan bero på felaktig konfiguration av "
 "serveradministratören eller ett temporärt fel. Var god fösök igen senare."
 
-#: lib/Chora.php:470
+#: lib/Chora.php:471
 msgid "Statistics"
 msgstr "Statistik"
 
@@ -381,7 +376,7 @@ msgstr "Statistik"
 msgid "Statistics for %s"
 msgstr "Statistik för %s"
 
-#: templates/log/rev.inc:34
+#: templates/log/rev.inc:32
 msgid "Tags:"
 msgstr "Märken:"
 
@@ -427,43 +422,43 @@ msgstr "Visa gren"
 msgid "View revisions on:"
 msgstr "Visa revisioner för:"
 
-#: lib/Chora.php:472
+#: lib/Chora.php:473
 msgid "View:"
 msgstr "Visa:"
 
-#: templates/directory/header.inc:18
+#: templates/directory/header.inc:16
 msgid "_Author"
 msgstr "_Författare"
 
-#: lib/Chora.php:465
+#: lib/Chora.php:466
 msgid "_Branches"
 msgstr "_Grenar"
 
-#: lib/Chora.php:439
+#: lib/Chora.php:440
 msgid "_Browse"
 msgstr "_Lista"
 
-#: templates/directory/header.inc:8
+#: templates/directory/header.inc:6
 msgid "_File"
 msgstr "_Fil"
 
-#: lib/Chora.php:468
+#: lib/Chora.php:469
 msgid "_Graph"
 msgstr "_Graf"
 
-#: lib/Chora.php:459
+#: lib/Chora.php:460
 msgid "_Logs"
 msgstr "_Loggar"
 
-#: lib/Chora.php:462
+#: lib/Chora.php:463
 msgid "_Patchsets"
 msgstr "_Patchsets"
 
-#: templates/directory/header.inc:13
+#: templates/directory/header.inc:11
 msgid "_Rev"
 msgstr "_Rev"
 
-#: lib/Chora.php:470
+#: lib/Chora.php:471
 msgid "_Statistics"
 msgstr "_Statistik"
 
@@ -471,7 +466,7 @@ msgstr "_Statistik"
 msgid "and:"
 msgstr "och:"
 
-#: templates/history/rev.inc:4 templates/log/rev.inc:5
+#: templates/log/rev.inc:5 templates/history/rev.inc:4
 #, php-format
 msgid "by %s"
 msgstr "vid %s"
diff --git a/po/zh_CN.po b/po/zh_CN.po
index cac4b94..b3e4080 100644
--- a/po/zh_CN.po
+++ b/po/zh_CN.po
@@ -1,5 +1,5 @@
 # SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR Horde Project
+# Copyright YEAR Horde Project
 # This file is distributed under the same license as the PACKAGE package.
 # liaobin <liaobin at jite.net>, 2004.
 #
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: chora\n"
 "Report-Msgid-Bugs-To: dev at lists.horde.org\n"
-"POT-Creation-Date: 2005-10-13 17:16+0200\n"
+"POT-Creation-Date: 2004-02-14 13:49+0100\n"
 "PO-Revision-Date: 2003-09-24 20:30+0800\n"
 "Last-Translator: liaobin <liaobin at jite.net>\n"
 "Language-Team: zh_CN <LL at li.org>\n"
@@ -19,7 +19,7 @@ msgstr ""
 msgid "#"
 msgstr "#"
 
-#: templates/patchsets/ps.inc:12 templates/log/rev.inc:26
+#: templates/patchsets/ps.inc:12 templates/log/rev.inc:24
 #, php-format
 msgid "%s ago"
 msgstr "%s ÒÔÇ°"
@@ -33,7 +33,7 @@ msgstr "
 msgid "All Branches"
 msgstr "ËùÓзÖÖ§"
 
-#: co.php:72 templates/log/rev.inc:8
+#: co.php:60 templates/log/rev.inc:8
 msgid "Annotate"
 msgstr "×¢ÊÍ"
 
@@ -46,16 +46,16 @@ msgstr "
 msgid "Back"
 msgstr "·µ»Ø"
 
-#: templates/checkout/checkout.inc:27 templates/log/rev.inc:31
+#: templates/checkout/checkout.inc:27 templates/log/rev.inc:29
 msgid "Branch Point for:"
 msgstr "·ÖÖ§µã: "
 
 #: templates/checkout/checkout.inc:11 templates/patchsets/ps.inc:14
-#: templates/log/rev.inc:28
+#: templates/log/rev.inc:26
 msgid "Branch:"
 msgstr "·ÖÖ§£º"
 
-#: lib/Chora.php:465
+#: lib/Chora.php:395
 msgid "Branches"
 msgstr "ËùÓзÖÖ§"
 
@@ -63,14 +63,18 @@ msgstr "
 msgid "Branching to"
 msgstr "·ÖÖ§µ½"
 
-#: templates/checkout/checkout.inc:35
+#: cvsgraph.php:48
 #, php-format
-msgid "Changed since <b>%s</b>"
-msgstr "×Ô<b>%s</b>µÄ±ä¸ü"
+msgid "CVS Graph for %s"
+msgstr "%s µÄCVSͼ"
 
-#: templates/log/rev.inc:37
-#, fuzzy, php-format
-msgid "Changed since <strong>%s</strong>"
+#: templates/checkout/checkout.inc:19 templates/patchsets/ps.inc:17
+msgid "CVS Tags:"
+msgstr "CVS ±êÇ©£º"
+
+#: templates/checkout/checkout.inc:35 templates/log/rev.inc:35
+#, php-format
+msgid "Changed since <b>%s</b>"
 msgstr "×Ô<b>%s</b>µÄ±ä¸ü"
 
 #: templates/history/rev.inc:8
@@ -78,7 +82,7 @@ msgstr "
 msgid "Changed: %s"
 msgstr "Òѱä¸ü£º%s"
 
-#: co.php:70
+#: co.php:58
 #, php-format
 msgid "Checkout of %s (revision %s)"
 msgstr "%s µÄCVSÈ¡³ö(°æ±¾ %s)"
@@ -93,15 +97,10 @@ msgstr "
 msgid "Click on the revisions and branches to display the file."
 msgstr "µã»÷Ìض¨°æ±¾ºÍ·ÖÖ§ÏÔʾÎļþ¡£"
 
-#: templates/log/rev.inc:42 templates/log/rev.inc:50
+#: templates/log/rev.inc:40 templates/log/rev.inc:49
 msgid "Colored"
 msgstr "ʹÓÃÑÕÉ«±ê¼Ç"
 
-#: templates/checkout/checkout.inc:19 templates/patchsets/ps.inc:17
-#, fuzzy
-msgid "Commit Tags:"
-msgstr "Ìá½»"
-
 #: templates/stats/stats.inc:4
 msgid "Commits"
 msgstr "Ìá½»"
@@ -115,19 +114,14 @@ msgid "Customize tasks to run upon logging in to Chora."
 msgstr "¿Í»§»¯ÈÎÎñÒÔ±ãÔڵǼµ½choraºóÔËÐС£"
 
 #: templates/directory/header.inc:23
-#, fuzzy
-msgid "Da_te"
-msgstr "ÈÕÆÚ"
-
-#: templates/directory/header.inc:23
 msgid "Date"
 msgstr "ÈÕÆÚ"
 
-#: patchsets.php:66
+#: patchsets.php:65
 msgid "Deleted"
 msgstr "ÒÑɾ³ý"
 
-#: templates/directory/file.inc:5 templates/directory/file.inc:17
+#: templates/directory/file.inc:6 templates/directory/file.inc:18
 msgid "Deleted File"
 msgstr "ÒÑɾ³ýÎļþ"
 
@@ -135,25 +129,25 @@ msgstr "
 msgid "Deselect"
 msgstr "È¡ÏûÑ¡Ôñ"
 
-#: patchsets.php:63
+#: patchsets.php:62
 msgid "Diff"
 msgstr "°æ±¾±È½Ï"
 
-#: patchsets.php:46
+#: patchsets.php:45
 msgid "Diff All Files"
 msgstr "±È½ÏËùÓÐÎļþ"
 
-#: diff.php:69
+#: diff.php:68
 #, php-format
 msgid "Diff for %s between version %s and %s"
 msgstr "¶Ô %s ÔÚ°æ±¾ %s ºÍ %s ¼ä±È½Ï"
 
-#: templates/log/rev.inc:49
-#, fuzzy, php-format
-msgid "Diffs to <strong>%s</strong>"
+#: templates/log/rev.inc:48
+#, php-format
+msgid "Diffs to <b>%s</b>"
 msgstr "ͬ<b>%s</b>±È½Ï"
 
-#: templates/log/rev.inc:41
+#: templates/log/rev.inc:39
 #, php-format
 msgid "Diffs to version %s"
 msgstr "ͬ°æ±¾ %s µÄ±È½Ï"
@@ -162,7 +156,7 @@ msgstr "ͬ
 msgid "Directory"
 msgstr "Ŀ¼"
 
-#: annotate.php:30 co.php:73 templates/log/rev.inc:10
+#: annotate.php:29 co.php:61 templates/log/rev.inc:10
 msgid "Download"
 msgstr "ÏÂÔØ"
 
@@ -178,7 +172,7 @@ msgstr "
 msgid "Error Encountered"
 msgstr "Óöµ½Ò»¸ö´íÎó"
 
-#: templates/directory/file.inc:7 templates/directory/file.inc:19
+#: templates/directory/file.inc:8 templates/directory/file.inc:20
 #: templates/directory/header.inc:8
 msgid "File"
 msgstr "Îļþ"
@@ -191,24 +185,18 @@ msgstr "
 msgid "Get Diffs"
 msgstr "»ñÈ¡²»Í¬"
 
-#: lib/Chora.php:468
+#: lib/Chora.php:398
 msgid "Graph"
 msgstr "°æ±¾Í¼"
 
-#: cvsgraph.php:48
-#, fuzzy, php-format
-msgid "Graph for %s"
-msgstr "%s µÄCVSͼ"
+#: templates/menu/menu.inc:21
+msgid "Help"
+msgstr "°ïÖú"
 
-#: browse.php:40
+#: cvs.php:40
 msgid "Hide Deleted Files"
 msgstr "Òþ²Øɾ³ýµÄÎļþ"
 
-#: browse.php:40
-#, fuzzy
-msgid "Hide _Deleted Files"
-msgstr "Òþ²Øɾ³ýµÄÎļþ"
-
 #: templates/log/request.inc:26
 msgid "Human Readable"
 msgstr "ÈË¿ÉÔĶÁ"
@@ -235,7 +223,7 @@ msgstr "
 msgid "Line %s"
 msgstr "ÐÐ %s"
 
-#: templates/headerbar.inc:11
+#: templates/headerbar.inc:12
 msgid "Location:"
 msgstr "λÖãº"
 
@@ -247,19 +235,19 @@ msgstr "
 msgid "Login Tasks"
 msgstr "µÇ¼ÈÎÎñ"
 
-#: lib/Chora.php:459
+#: lib/Chora.php:389
 msgid "Logs"
 msgstr "ÈÕÖ¾"
 
-#: templates/log/rev.inc:43 templates/log/rev.inc:51
+#: templates/log/rev.inc:41 templates/log/rev.inc:50
 msgid "Long"
 msgstr "³¤¸ñʽ"
 
-#: diff.php:61
+#: diff.php:60
 msgid "Malformed Query"
 msgstr "²»ÍêÕûµÄ²éѯ"
 
-#: patchsets.php:59
+#: patchsets.php:58
 msgid "New File"
 msgstr "ÎļþÎļþ"
 
@@ -267,15 +255,19 @@ msgstr "
 msgid "No Viewable Change"
 msgstr "ûÓпÉÒԲ鿴µÄ¸ü¸Ä"
 
-#: templates/log/rev.inc:44 templates/log/rev.inc:52
+#: templates/log/rev.inc:42 templates/log/rev.inc:51
 msgid "NoWhitespaceChanges"
 msgstr "ûÓпոñµÄ¸ü¸Ä"
 
+#: templates/menu/menu.inc:13
+msgid "Options"
+msgstr "Ñ¡Ïî"
+
 #: config/prefs.php.dist:9
 msgid "Other Options"
 msgstr "ÆäËûÑ¡Ïî"
 
-#: lib/Chora.php:368
+#: lib/Chora.php:304
 msgid "Other Repositories"
 msgstr "ÆäËû²Ö¿â"
 
@@ -288,11 +280,11 @@ msgstr "
 msgid "PatchSet %s</span> by %s"
 msgstr "²¹¶¡%s</span>ÓÉ%s²úÉú"
 
-#: lib/Chora.php:462
+#: lib/Chora.php:392
 msgid "Patchsets"
 msgstr "²¹¶¡"
 
-#: patchsets.php:27
+#: patchsets.php:26
 #, php-format
 msgid "Patchsets for %s"
 msgstr "%sµÄ²¹¶¡"
@@ -314,7 +306,7 @@ msgstr "
 msgid "Rev"
 msgstr "°æ±¾"
 
-#: co.php:77
+#: co.php:65
 #, php-format
 msgid "Revision %s for file %s not found."
 msgstr "°æ±¾%s(Îļþ%s)ûÓÐÕÒµ½¡£"
@@ -328,15 +320,10 @@ msgstr "
 msgid "Select for Diff"
 msgstr "Ñ¡Ôñºó²ì¿´¸ü¸Ä"
 
-#: browse.php:42
+#: cvs.php:42
 msgid "Show Deleted Files"
 msgstr "ÏÔʾÒѾ­É¾³ýµÄÎļþ"
 
-#: browse.php:42
-#, fuzzy
-msgid "Show _Deleted Files"
-msgstr "ÏÔʾÒѾ­É¾³ýµÄÎļþ"
-
 #: templates/log/request.inc:29
 msgid "Side-by-Side"
 msgstr "²¢¼çÏÔʾ"
@@ -346,27 +333,27 @@ msgstr "
 msgid "Sort Order"
 msgstr "ÅÅÐò˳Ðò"
 
-#: annotate.php:27
+#: annotate.php:26
 #, php-format
 msgid "Source Annotation of %s for version %s"
 msgstr "%s µÄ×¢ÊÍ(°æ±¾ %s)"
 
-#: history.php:160
+#: history.php:159
 #, php-format
 msgid "Source Branching View for %s"
 msgstr "%s µÄ°æ±¾·Ö֧ͼ"
 
-#: browse.php:32
+#: cvs.php:32
 #, php-format
 msgid "Source Directory of /%s"
 msgstr "/%s µÄĿ¼"
 
-#: browse.php:126
+#: cvs.php:125
 #, php-format
 msgid "Source Log for %s"
 msgstr "%s µÄÈÕÖ¾"
 
-#: lib/Chora.php:182
+#: lib/Chora.php:125
 msgid ""
 "SourceRoot not found! This could be a misconfiguration by the server "
 "administrator, or the server could be having temporary problems. Please try "
@@ -375,16 +362,16 @@ msgstr ""
 "Ô´¸ùĿ¼ûÓÐÕÒµ½£¡Õâ¿ÉÄÜÊÇ·þÎñÆ÷¹ÜÀíÔ±µÄ´íÎóÅäÖûò·þÎñÆ÷ÁãʱÎÊÌâÔì³ÉµÄ¡£ÇëÉÔ"
 "ºòÔÙÊÔ¡£"
 
-#: lib/Chora.php:470
+#: lib/Chora.php:400
 msgid "Statistics"
 msgstr "ͳ¼Æ"
 
-#: stats.php:27
+#: stats.php:26
 #, php-format
 msgid "Statistics for %s"
 msgstr "Ϊ%sͳ¼Æ"
 
-#: templates/log/rev.inc:34
+#: templates/log/rev.inc:32
 msgid "Tags:"
 msgstr "±êÇ©£º"
 
@@ -397,7 +384,7 @@ msgstr ""
 "Õâ¸ö±í¸ñÔÊÐíÄú²éѯһ¸öÎļþµÄ²»Í¬°æ±¾Ö®¼äµÄ²îÒì¡£Äú¿ÉÒÔʹÓÃÑ¡Ôñ¿òÑ¡ÔñÒ»¸ö°æ±¾"
 "Ãû³Æ»òÄú¿ÉÒÔÔÚÎı¾¿òÖÐÊäÈëÒ»¸ö°æ±¾ºÅ¡£"
 
-#: templates/headerbar.inc:16
+#: templates/headerbar.inc:17
 msgid "Tracking Branch"
 msgstr "¸ú×Ù·ÖÖ§"
 
@@ -417,7 +404,11 @@ msgstr "ʹ
 msgid "Use last viewed file or directory at login time"
 msgstr "ÔڵǼºóʹÓÃ×îºó²é¿´µÄÎļþ»òĿ¼"
 
-#: annotate.php:29 templates/log/rev.inc:9
+#: prefs.php:24
+msgid "User Options"
+msgstr "Óû§Ñ¡Ïî"
+
+#: annotate.php:28 templates/log/rev.inc:9
 msgid "View"
 msgstr "²ì¿´"
 
@@ -429,54 +420,10 @@ msgstr "
 msgid "View revisions on:"
 msgstr "²ì¿´°æ±¾ÔÚ£º"
 
-#: lib/Chora.php:472
+#: lib/Chora.php:402
 msgid "View:"
 msgstr "²ì¿´:"
 
-#: templates/directory/header.inc:18
-#, fuzzy
-msgid "_Author"
-msgstr "×÷Õß"
-
-#: lib/Chora.php:465
-#, fuzzy
-msgid "_Branches"
-msgstr "ËùÓзÖÖ§"
-
-#: lib/Chora.php:439
-msgid "_Browse"
-msgstr ""
-
-#: templates/directory/header.inc:8
-#, fuzzy
-msgid "_File"
-msgstr "Îļþ"
-
-#: lib/Chora.php:468
-#, fuzzy
-msgid "_Graph"
-msgstr "°æ±¾Í¼"
-
-#: lib/Chora.php:459
-#, fuzzy
-msgid "_Logs"
-msgstr "ÈÕÖ¾"
-
-#: lib/Chora.php:462
-#, fuzzy
-msgid "_Patchsets"
-msgstr "²¹¶¡"
-
-#: templates/directory/header.inc:13
-#, fuzzy
-msgid "_Rev"
-msgstr "°æ±¾"
-
-#: lib/Chora.php:470
-#, fuzzy
-msgid "_Statistics"
-msgstr "ͳ¼Æ"
-
 #: templates/log/request.inc:35
 msgid "and:"
 msgstr "ºÍ£º"
@@ -490,78 +437,16 @@ msgstr "
 msgid "changed lines"
 msgstr "¸ü¸ÄµÄÐÐÊý"
 
-#: lib/Chora.php:31
-msgid "day"
-msgstr ""
-
-#: lib/Chora.php:31
-msgid "days"
-msgstr ""
-
 #: templates/error_page.inc:14
 msgid "for further information."
 msgstr "ΪδÀ´ÐÅÏ¢¡£"
 
-#: lib/Chora.php:30
-#, fuzzy
-msgid "hour"
-msgstr "×÷Õß"
-
-#: lib/Chora.php:30
-msgid "hours"
-msgstr ""
-
-#: lib/Chora.php:29
-#, fuzzy
-msgid "minute"
-msgstr "ÐÐ"
-
-#: lib/Chora.php:29
-msgid "minutes"
-msgstr ""
-
-#: lib/Chora.php:33
-msgid "month"
-msgstr ""
-
-#: lib/Chora.php:33
-msgid "months"
-msgstr ""
-
 #: templates/history/branch_cell.inc:6
 #, php-format
 msgid "revision %s"
 msgstr "°æ±¾ %s"
 
-#: lib/Chora.php:28
-msgid "second"
-msgstr ""
-
-#: lib/Chora.php:28
-msgid "seconds"
-msgstr ""
-
 #: templates/diff/hr/header.inc:13 templates/diff/hr/header.inc:14
 #, php-format
 msgid "version %s"
 msgstr "°æ±¾ %s"
-
-#: lib/Chora.php:42
-msgid "very little time"
-msgstr ""
-
-#: lib/Chora.php:32
-msgid "week"
-msgstr ""
-
-#: lib/Chora.php:32
-msgid "weeks"
-msgstr ""
-
-#: lib/Chora.php:34
-msgid "year"
-msgstr ""
-
-#: lib/Chora.php:34
-msgid "years"
-msgstr ""
diff --git a/po/zh_TW.po b/po/zh_TW.po
index 49b19c5..a2c3013 100644
--- a/po/zh_TW.po
+++ b/po/zh_TW.po
@@ -1,13 +1,12 @@
 # Chora Chinese Translation
-# Copyright (C) 2003. Lin Zhemin.
+# Copyright 2003. Lin Zhemin.
 # This file is distributed under the same license as the PACKAGE package.
 # Lin Zhemin <ljm at ljm.idv.tw> 2003
 #
 msgid ""
 msgstr ""
 "Project-Id-Version: Chora 1.2\n"
-"Report-Msgid-Bugs-To: dev at lists.horde.org\n"
-"POT-Creation-Date: 2005-10-13 17:16+0200\n"
+"POT-Creation-Date: 2003-01-03 14:30+0100\n"
 "PO-Revision-Date: 2003-01-24 11:25+0100\n"
 "Last-Translator: Lin Zhemin <ljm at ljm.idv.tw>\n"
 "Language-Team: Greenworld Chinese <ljm at sunup.net>\n"
@@ -15,11 +14,20 @@ msgstr ""
 "Content-Type: text/plain; charset=Big5\n"
 "Content-Transfer-Encoding: 8bit\n"
 
+#: templates/log/request.inc:5
+msgid ""
+" This form allows you to request diffs between any two revisions of a file.  "
+"You may select a symbolic revision name using the selection box or you may "
+"type in a numeric name using the type-in text box."
+msgstr ""
+"§A¥i¥H¦b¥»ªí³æ¿ï¾Ü¥ô·N¨â­Óª©¥»¡A²£¥Í¥¦­Ìªº diff ÀÉ. ½Ð¦b¿ï³æ¤¤¿ï¾Ü symbolic "
+"ª©¥»©Î¦b¤å¦r®Ø¸Ì¿é¤Jª©¥»¼Æ¦r. "
+
 #: templates/annotate/header.inc:4
 msgid "#"
 msgstr "#"
 
-#: templates/patchsets/ps.inc:12 templates/log/rev.inc:26
+#: templates/log/rev.inc:14
 #, php-format
 msgid "%s ago"
 msgstr "%s ¥H«e"
@@ -33,12 +41,15 @@ msgstr "
 msgid "All Branches"
 msgstr "¨C­Ó¤ÀªK"
 
-#: co.php:72 templates/log/rev.inc:8
+#: prefs.php:77
+msgid "An illegal value was specified."
+msgstr "§A«ü©w¤F¿ù»~ªº­È."
+
+#: co.php:65 templates/log/rev.inc:8
 msgid "Annotate"
 msgstr "¥[µù"
 
-#: templates/directory/header.inc:18 templates/stats/stats.inc:3
-#: templates/annotate/header.inc:5
+#: templates/annotate/header.inc:5 templates/directory/header.inc:17
 msgid "Author"
 msgstr "§@ªÌ"
 
@@ -46,119 +57,101 @@ msgstr "
 msgid "Back"
 msgstr ""
 
-#: templates/checkout/checkout.inc:27 templates/log/rev.inc:31
+#: templates/log/rev.inc:23
 msgid "Branch Point for:"
 msgstr ""
 
-#: templates/checkout/checkout.inc:11 templates/patchsets/ps.inc:14
-#: templates/log/rev.inc:28
+#: templates/log/rev.inc:16
 msgid "Branch:"
 msgstr "¤ÀªK:"
 
-#: lib/Chora.php:465
-#, fuzzy
-msgid "Branches"
-msgstr "¨C­Ó¤ÀªK"
-
 #: templates/history/branch_cell.inc:2
-#, fuzzy
 msgid "Branching to"
-msgstr "¤ÀªK:"
+msgstr ""
 
-#: templates/checkout/checkout.inc:35
+#: annotate.php:26
 #, php-format
-msgid "Changed since <b>%s</b>"
-msgstr "¬Û¹ï©ó <b>v.%s</b> §ó°Ê¤F"
-
-#: templates/log/rev.inc:37
-#, fuzzy, php-format
-msgid "Changed since <strong>%s</strong>"
-msgstr "¬Û¹ï©ó <b>v.%s</b> §ó°Ê¤F"
+msgid "CVS Annotation of %s for version %s"
+msgstr "CVS µù¸Ñ - %s v.%s"
 
-#: templates/history/rev.inc:8
+#: history.php:153
 #, php-format
-msgid "Changed: %s"
-msgstr "§ó°Ê¦æ¼Æ %s"
+msgid "CVS Branching View for %s"
+msgstr "CVS ¤ÀªK°O¿ý - %s"
 
-#: co.php:70
-#, fuzzy, php-format
-msgid "Checkout of %s (revision %s)"
+#: co.php:63
+#, php-format
+msgid "CVS Checkout of %s (revision %s)"
 msgstr "¬d¬Ý CVS ÀÉ - %s (%s ª©)"
 
-#: templates/cvsgraph/cvsgraph.inc:11
-msgid ""
-"Click on the links in between revisions to get a diff between those "
-"revisions."
-msgstr ""
-
-#: templates/cvsgraph/cvsgraph.inc:10
-msgid "Click on the revisions and branches to display the file."
-msgstr ""
+#: cvs.php:35
+#, php-format
+msgid "CVS Directory of /%s"
+msgstr "CVS ¥Ø¿ý /%s"
 
-#: templates/log/rev.inc:42 templates/log/rev.inc:50
-msgid "Colored"
-msgstr ""
+#: cvs.php:123
+#, php-format
+msgid "CVS Log for %s"
+msgstr "CVS °O¿ý - %s"
 
-#: templates/checkout/checkout.inc:19 templates/patchsets/ps.inc:17
-#, fuzzy
-msgid "Commit Tags:"
+#: templates/log/rev.inc:19
+msgid "CVS Tags:"
 msgstr "CVS ¼Ð°O:"
 
-#: templates/stats/stats.inc:4
-msgid "Commits"
-msgstr ""
+#: templates/log/rev.inc:27
+#, php-format
+msgid "Changed since <b>%s</b>"
+msgstr "¬Û¹ï©ó <b>v.%s</b> §ó°Ê¤F"
+
+#: templates/history/rev.inc:8
+#, php-format
+msgid "Changed: %s"
+msgstr "§ó°Ê¦æ¼Æ %s"
+
+#: templates/headerbar.inc:11
+msgid "Chora Homepage"
+msgstr "Chora ­º­¶"
+
+#: templates/index/notconfigured.inc:4
+msgid "Chora is not properly configured"
+msgstr "Chora ¨S¦³³]©w¦n"
 
 #: templates/log/request.inc:28
 msgid "Context"
 msgstr "¥]¬A¤W¤U¼Æ¦æ"
 
-#: config/prefs.php.dist:11
+#: lib/CVSLib/Checkout.php:43
+msgid "Couldn't perform checkout of the requested file"
+msgstr "¤£¯à checkout ÀÉ®×"
+
+#: config/prefs.php.dist:7
 msgid "Customize tasks to run upon logging in to Chora."
 msgstr "µn¤J Chora «á­n°õ¦æªº¤u§@"
 
-#: templates/directory/header.inc:23
-#, fuzzy
-msgid "Da_te"
-msgstr "®É¶¡"
-
-#: templates/directory/header.inc:23
+#: templates/directory/header.inc:21
 msgid "Date"
 msgstr "®É¶¡"
 
-#: patchsets.php:66
-#, fuzzy
-msgid "Deleted"
-msgstr "Àɮפw§R°£"
-
-#: templates/directory/file.inc:5 templates/directory/file.inc:17
+#: templates/directory/file.inc:6 templates/directory/file.inc:18
 msgid "Deleted File"
 msgstr "Àɮפw§R°£"
 
-#: templates/log/rev.inc:13
-#, fuzzy
-msgid "Deselect"
-msgstr "¨ú®ø¿ï¾Ü"
-
-#: patchsets.php:63
-#, fuzzy
-msgid "Diff"
-msgstr "¨ú¦^ diff ÀÉ"
-
-#: patchsets.php:46
-msgid "Diff All Files"
-msgstr ""
-
-#: diff.php:69
+#: diff.php:66
 #, php-format
 msgid "Diff for %s between version %s and %s"
 msgstr "Diff ÀÉ - %s v.%s ©M v.%s ªº®t²§"
 
-#: templates/log/rev.inc:49
+#: templates/log/rev.inc:42
 #, php-format
-msgid "Diffs to <strong>%s</strong>"
-msgstr ""
+msgid "Diffs ignoring whitespace to version %s"
+msgstr "¹ï v.%s ªº diff(©¿²¤ªÅ¥Õ)"
+
+#: templates/log/rev.inc:48
+#, php-format
+msgid "Diffs to selected version %s"
+msgstr "¹ï©Ò¿ïªº v.%s ªº diff"
 
-#: templates/log/rev.inc:41
+#: templates/log/rev.inc:38
 #, php-format
 msgid "Diffs to version %s"
 msgstr "¹ï v.%s ªº diff"
@@ -167,7 +160,7 @@ msgstr "
 msgid "Directory"
 msgstr "¥Ø¿ý"
 
-#: annotate.php:30 co.php:73 templates/log/rev.inc:10
+#: annotate.php:29 co.php:66 templates/log/rev.inc:10
 msgid "Download"
 msgstr "¤U¸ü"
 
@@ -175,7 +168,7 @@ msgstr "
 msgid "Ed Script"
 msgstr "Ed ¸}¥»"
 
-#: templates/error_page.inc:4
+#: status.php:19 templates/error_page.inc:4
 msgid "Error"
 msgstr "¿ù»~"
 
@@ -183,43 +176,36 @@ msgstr "
 msgid "Error Encountered"
 msgstr "¹J¨ì¤F¿ù»~"
 
-#: templates/directory/file.inc:7 templates/directory/file.inc:19
-#: templates/directory/header.inc:8
+#: lib/CVSLib/File.php:218
+msgid "Failed to spawn rlog to retrieve file log information"
+msgstr "µLªk°õ¦æ rlog ¨ú¦^ÀÉ®×°O¿ý"
+
+#: templates/directory/file.inc:8 templates/directory/file.inc:20
+#: templates/directory/header.inc:9
 msgid "File"
 msgstr "ÀÉ®×"
 
-#: templates/patchsets/ps.inc:21
-#, fuzzy
-msgid "Files Changed:"
-msgstr "§ó°Ê¦æ¼Æ %s"
-
 #: templates/log/request.inc:44
 msgid "Get Diffs"
 msgstr "¨ú¦^ diff ÀÉ"
 
-#: lib/Chora.php:468
-msgid "Graph"
-msgstr ""
-
-#: cvsgraph.php:48
-#, php-format
-msgid "Graph for %s"
-msgstr ""
+#: templates/menu/menu.inc:26
+msgid "Help"
+msgstr "¨D§U"
 
-#: browse.php:40
+#: cvs.php:39
 msgid "Hide Deleted Files"
 msgstr "ÁôÂçR°£ªºÀÉ®×"
 
-#: browse.php:40
-#, fuzzy
-msgid "Hide _Deleted Files"
-msgstr "ÁôÂçR°£ªºÀÉ®×"
-
 #: templates/log/request.inc:26
 msgid "Human Readable"
 msgstr "¤H¬Ý±oÀ´ªº®æ¦¡"
 
-#: templates/directory/header.inc:25
+#: lib/CVSLib/Checkout.php:30
+msgid "Invalid revision number"
+msgstr "ª©¥»¸¹½X¤£¹ï"
+
+#: templates/directory/header.inc:23
 msgid "Last Log"
 msgstr "³Ì«á¤@¦¸°O¿ý"
 
@@ -241,50 +227,35 @@ msgstr "
 msgid "Line %s"
 msgstr "²Ä %s ¦æ"
 
-#: templates/headerbar.inc:11
+#: templates/headerbar.inc:26
 msgid "Location:"
 msgstr "¦ì¸m:"
 
-#: templates/checkout/checkout.inc:42
-msgid "Log:"
-msgstr ""
-
-#: config/prefs.php.dist:10
+#: config/prefs.php.dist:6
 msgid "Login Tasks"
 msgstr "µn¤J¤u§@"
 
-#: lib/Chora.php:459
-msgid "Logs"
-msgstr ""
-
-#: templates/log/rev.inc:43 templates/log/rev.inc:51
-#, fuzzy
-msgid "Long"
-msgstr "ªø®æ¦¡"
-
-#: diff.php:61
-msgid "Malformed Query"
-msgstr ""
-
-#: patchsets.php:59
-#, fuzzy
-msgid "New File"
-msgstr "ÀÉ®×"
+#: status.php:32
+msgid "Message"
+msgstr "«H®§"
 
 #: templates/diff/hr/nochange.inc:6
 msgid "No Viewable Change"
 msgstr "¨S¦³¥iÂsÄýªº§ó°Ê"
 
-#: templates/log/rev.inc:44 templates/log/rev.inc:52
-#, fuzzy
-msgid "NoWhitespaceChanges"
-msgstr "¨S¦³¥iÂsÄýªº§ó°Ê"
+#: lib/CVSLib/File.php:124 lib/CVSLib/File.php:147
+msgid "No revisions"
+msgstr "¨S¦³ª©¥»"
 
-#: config/prefs.php.dist:9
+#: templates/menu/menu.inc:13
+msgid "Options"
+msgstr "¿ï¶µ"
+
+#: config/prefs.php.dist:5
 msgid "Other Options"
 msgstr "¨ä¥L¿ï¶µ"
 
-#: lib/Chora.php:368
+#: lib/Chora.php:349
 msgid "Other Repositories"
 msgstr "¨ä¥L¥Ø¿ý"
 
@@ -292,24 +263,14 @@ msgstr "
 msgid "Parent Directory"
 msgstr "¤W¼h¥Ø¿ý"
 
-#: templates/patchsets/ps.inc:6
-#, php-format
-msgid "PatchSet %s</span> by %s"
-msgstr ""
-
-#: lib/Chora.php:462
-msgid "Patchsets"
-msgstr ""
-
-#: patchsets.php:27
-#, php-format
-msgid "Patchsets for %s"
-msgstr ""
-
 #: templates/error_page.inc:13
 msgid "Please contact"
 msgstr "½Ð³sµ¸"
 
+#: templates/menu/menu.inc:18
+msgid "Problem?"
+msgstr "¦³°ÝÃD¶Ü?"
+
 #: templates/diff/hr/footer.inc:11
 #, php-format
 msgid "Removed in v.%s"
@@ -319,97 +280,81 @@ msgstr "
 msgid "Retrieve diffs between:"
 msgstr "²£¥Í³oª©:"
 
-#: templates/directory/header.inc:13 templates/annotate/header.inc:6
+#: templates/annotate/header.inc:6 templates/directory/header.inc:13
 msgid "Rev"
 msgstr "ª©¥»"
 
-#: co.php:77
-#, php-format
-msgid "Revision %s for file %s not found."
-msgstr ""
-
-#: templates/checkout/checkout.inc:4
-#, php-format
-msgid "Revision <b>%s</b>, <i>%s</i> (%s ago) by %s"
-msgstr ""
-
-#: templates/log/rev.inc:15
+#: templates/log/rev.inc:33
 msgid "Select for Diff"
 msgstr "¿ï¾Ü°µdiffÀÉ"
 
-#: browse.php:42
+#: cvs.php:41
 msgid "Show Deleted Files"
 msgstr "Åã¥Ü§R°£ªºÀÉ®×"
 
-#: browse.php:42
-#, fuzzy
-msgid "Show _Deleted Files"
-msgstr "Åã¥Ü§R°£ªºÀÉ®×"
-
 #: templates/log/request.inc:29
 msgid "Side-by-Side"
 msgstr "¨âÃä¤À¦C"
 
-#: templates/directory/header.inc:7 templates/directory/header.inc:12
-#: templates/directory/header.inc:17 templates/directory/header.inc:22
+#: templates/index/notconfigured.inc:13
+msgid "Some of Chora's configuration files are missing:"
+msgstr "³o¨Ç Chora ³]©wÀɤ£¨£¤F:"
+
+#: templates/directory/header.inc:8 templates/directory/header.inc:12
+#: templates/directory/header.inc:16 templates/directory/header.inc:20
 msgid "Sort Order"
 msgstr "±Æ§Ç"
 
-#: annotate.php:27
-#, fuzzy, php-format
-msgid "Source Annotation of %s for version %s"
-msgstr "CVS µù¸Ñ - %s v.%s"
+#: status.php:23
+msgid "Success"
+msgstr "¦n¤F"
 
-#: history.php:160
-#, fuzzy, php-format
-msgid "Source Branching View for %s"
-msgstr "CVS ¤ÀªK°O¿ý - %s"
+#: cvs.php:131
+msgid "Switch to Branch View"
+msgstr "¤Á¨ì¤ÀªK°O¿ý"
 
-#: browse.php:32
-#, fuzzy, php-format
-msgid "Source Directory of /%s"
-msgstr "CVS ¥Ø¿ý /%s"
+#: history.php:155
+msgid "Switch to Log View"
+msgstr "¤Á¨ìª©¥»°O¿ý"
 
-#: browse.php:126
-#, fuzzy, php-format
-msgid "Source Log for %s"
-msgstr "CVS °O¿ý - %s"
-
-#: lib/Chora.php:182
+#: templates/index/notconfigured.inc:46
 msgid ""
-"SourceRoot not found! This could be a misconfiguration by the server "
-"administrator, or the server could be having temporary problems. Please try "
-"again later."
+"This file controls the default preferences for Chora, and also controls "
+"which preferences users can alter."
 msgstr ""
 
-#: lib/Chora.php:470
-msgid "Statistics"
+#: templates/index/notconfigured.inc:39
+msgid ""
+"This file controls the stylesheet that is used to set colors and fonts in "
+"addition to or overriding Horde defaults."
 msgstr ""
 
-#: stats.php:27
-#, php-format
-msgid "Statistics for %s"
+#: templates/index/notconfigured.inc:25
+msgid ""
+"This file defines all of the cvs repositories that you wish Chora to display."
 msgstr ""
 
-#: templates/log/rev.inc:34
-#, fuzzy
-msgid "Tags:"
-msgstr "CVS ¼Ð°O:"
+#: templates/index/notconfigured.inc:32
+msgid "This file defines any special MIME-type handling for Chora."
+msgstr ""
 
-#: templates/log/request.inc:5
-#, fuzzy
+#: templates/index/notconfigured.inc:18
 msgid ""
-"This form allows you to request diffs between any two revisions of a file.  "
-"You may select a symbolic revision name using the selection box or you may "
-"type in a numeric name using the type-in text box."
+"This is the main Chora configuration file. It contains paths and options for "
+"all Chora scripts."
 msgstr ""
-"§A¥i¥H¦b¥»ªí³æ¿ï¾Ü¥ô·N¨â­Óª©¥»¡A²£¥Í¥¦­Ìªº diff ÀÉ. ½Ð¦b¿ï³æ¤¤¿ï¾Ü symbolic "
-"ª©¥»©Î¦b¤å¦r®Ø¸Ì¿é¤Jª©¥»¼Æ¦r. "
 
-#: templates/headerbar.inc:16
-#, fuzzy
+#: prefs.php:86
+msgid "This number must be at least one."
+msgstr "³o­Ó¼Æ¦r­n¤j©óµ¥©ó 1."
+
+#: prefs.php:84
+msgid "This value must be a number."
+msgstr "½Ð¶ñ¤J¼Æ¦r."
+
+#: templates/headerbar.inc:32
 msgid "Tracking Branch"
-msgstr "ÂsÄý¤ÀªK°O¿ý"
+msgstr ""
 
 #: templates/log/request.inc:23
 msgid "Type:"
@@ -419,15 +364,23 @@ msgstr "
 msgid "Unified"
 msgstr "²Î¤@®æ¦¡"
 
+#: templates/log/rev.inc:31
+msgid "Unselect"
+msgstr "¨ú®ø¿ï¾Ü"
+
 #: templates/log/request.inc:18 templates/log/request.inc:38
 msgid "Use Text Field"
 msgstr "½Ð¿ï¾Ü©Î¦b¥kÃä¿é¤J"
 
-#: config/prefs.php.dist:37
+#: config/prefs.php.dist:33
 msgid "Use last viewed file or directory at login time"
 msgstr "µn¤J®Éª½±µÂsÄý¤W¦¸¬Ý¹LªºÀɮשΥؿý"
 
-#: annotate.php:29 templates/log/rev.inc:9
+#: prefs.php:133
+msgid "User Options"
+msgstr "¿ï¶µ"
+
+#: annotate.php:28 templates/log/rev.inc:9
 msgid "View"
 msgstr "¬d¬Ý"
 
@@ -439,56 +392,19 @@ msgstr "
 msgid "View revisions on:"
 msgstr "ÂsÄýª©¥»°O¿ý:"
 
-#: lib/Chora.php:472
-#, fuzzy
-msgid "View:"
-msgstr "¬d¬Ý"
-
-#: templates/directory/header.inc:18
-#, fuzzy
-msgid "_Author"
-msgstr "§@ªÌ"
-
-#: lib/Chora.php:465
-#, fuzzy
-msgid "_Branches"
-msgstr "¨C­Ó¤ÀªK"
-
-#: lib/Chora.php:439
-msgid "_Browse"
-msgstr ""
-
-#: templates/directory/header.inc:8
-#, fuzzy
-msgid "_File"
-msgstr "ÀÉ®×"
-
-#: lib/Chora.php:468
-msgid "_Graph"
-msgstr ""
-
-#: lib/Chora.php:459
-msgid "_Logs"
-msgstr ""
+#: status.php:27
+msgid "Warning"
+msgstr "ĵ§i"
 
-#: lib/Chora.php:462
-msgid "_Patchsets"
-msgstr ""
-
-#: templates/directory/header.inc:13
-#, fuzzy
-msgid "_Rev"
-msgstr "ª©¥»"
-
-#: lib/Chora.php:470
-msgid "_Statistics"
-msgstr ""
+#: prefs.php:118
+msgid "Your options have been updated."
+msgstr "¿ï¶µ§ó·s¦n¤F."
 
 #: templates/log/request.inc:35
 msgid "and:"
 msgstr "©M³oª©ªº diff ÀÉ:"
 
-#: templates/history/rev.inc:4 templates/log/rev.inc:5
+#: templates/log/rev.inc:6 templates/history/rev.inc:4
 #, php-format
 msgid "by %s"
 msgstr "§@ªÌ %s"
@@ -497,11 +413,11 @@ msgstr "
 msgid "changed lines"
 msgstr "§ó°Ê"
 
-#: lib/Chora.php:31
+#: lib/CVSLib/File.php:292
 msgid "day"
 msgstr "¤Ñ"
 
-#: lib/Chora.php:31
+#: lib/CVSLib/File.php:292
 msgid "days"
 msgstr "¤Ñ"
 
@@ -509,27 +425,31 @@ msgstr "
 msgid "for further information."
 msgstr "¥HÀò±o¶i¤@¨Bªº«H®§."
 
-#: lib/Chora.php:30
+#: lib/CVSLib/File.php:291
 msgid "hour"
 msgstr "¤p®É"
 
-#: lib/Chora.php:30
+#: lib/CVSLib/File.php:291
 msgid "hours"
 msgstr "¤p®É"
 
-#: lib/Chora.php:29
+#: templates/log/rev.inc:39 templates/log/rev.inc:43 templates/log/rev.inc:49
+msgid "long"
+msgstr "ªø®æ¦¡"
+
+#: lib/CVSLib/File.php:290
 msgid "minute"
 msgstr "¤À"
 
-#: lib/Chora.php:29
+#: lib/CVSLib/File.php:290
 msgid "minutes"
 msgstr "¤À"
 
-#: lib/Chora.php:33
+#: lib/CVSLib/File.php:294
 msgid "month"
 msgstr "­Ó¤ë"
 
-#: lib/Chora.php:33
+#: lib/CVSLib/File.php:294
 msgid "months"
 msgstr "­Ó¤ë"
 
@@ -538,35 +458,39 @@ msgstr "
 msgid "revision %s"
 msgstr "ª©¥» %s"
 
-#: lib/Chora.php:28
+#: lib/CVSLib/File.php:289
 msgid "second"
 msgstr "’"
 
-#: lib/Chora.php:28
+#: lib/CVSLib/File.php:289
 msgid "seconds"
 msgstr "’"
 
+#: templates/log/rev.inc:40 templates/log/rev.inc:44 templates/log/rev.inc:50
+msgid "unified"
+msgstr "²Î¤@®æ¦¡"
+
 #: templates/diff/hr/header.inc:13 templates/diff/hr/header.inc:14
 #, php-format
 msgid "version %s"
 msgstr "%s ª©"
 
-#: lib/Chora.php:42
+#: lib/CVSLib/File.php:303
 msgid "very little time"
 msgstr "¤£¤["
 
-#: lib/Chora.php:32
+#: lib/CVSLib/File.php:293
 msgid "week"
 msgstr "¶g"
 
-#: lib/Chora.php:32
+#: lib/CVSLib/File.php:293
 msgid "weeks"
 msgstr "¶g"
 
-#: lib/Chora.php:34
+#: lib/CVSLib/File.php:295
 msgid "year"
 msgstr "¦~"
 
-#: lib/Chora.php:34
+#: lib/CVSLib/File.php:295
 msgid "years"
 msgstr "¦~"
diff --git a/stats.php b/stats.php
index 125ffd9..eb9f96b 100644
--- a/stats.php
+++ b/stats.php
@@ -1,17 +1,18 @@
 <?php
 /**
- * $Horde: chora/stats.php,v 1.26.8.3 2007/01/02 13:54:05 jan Exp $
+ * $Horde: chora/stats.php,v 1.26.8.5 2009/01/06 15:22:34 jan Exp $
  *
- * Copyright 2000-2007 Anil Madhavapeddy <anil at recoil.org>
+ * Copyright 2000-2009 The Horde Project (http://www.horde.org/)
  *
- * See the enclosed file COPYING for license information (GPL).  If you
+ * 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  Anil Madhavapeddy <avsm at horde.org>
  */
 
 @define('CHORA_BASE', dirname(__FILE__));
 require_once CHORA_BASE . '/lib/base.php';
 
-/* Spawn the file object. */
 $fl = &$VC->getFileObject($where, $cache);
 Chora::checkError($fl);
 
@@ -23,8 +24,9 @@ foreach ($fl->logs as $lg) {
 }
 arsort($author_stats);
 
-// Show the page.
 $title = sprintf(_("Statistics for %s"), Text::htmlallspaces($where));
+Horde::addScriptFile('prototype.js', 'chora', true);
+Horde::addScriptFile('tables.js', 'chora', true);
 require CHORA_TEMPLATES . '/common-header.inc';
 require CHORA_TEMPLATES . '/menu.inc';
 require CHORA_TEMPLATES . '/headerbar.inc';
diff --git a/templates/annotate/footer.inc b/templates/annotate/footer.inc
index 000ca4b..db7876d 100644
--- a/templates/annotate/footer.inc
+++ b/templates/annotate/footer.inc
@@ -1 +1,42 @@
+</tbody>
 </table>
+<script type="text/javascript">
+//<![CDATA[
+function setRevisionLogTitles()
+{
+    var revisionLogMap = [];
+    <?php
+    foreach ($revList as $lg) {
+        // The code below attempts to account for the following:
+        //   1) log text with line breaks.
+        //   2) log text with inline HTML.
+        $logmsg = Text_Filter::filter($lg->log, 'text2html', array('parselevel' => TEXT_HTML_NOHTML, 'charset' => NLS::getCharset(), 'class' => ''));
+
+        // The above function converts newlines to HTML, and we don't
+        // want to escape those. Since the entire text is enclosed in
+        // single-quotes in JavaScipt, it should suffice to simply
+        // escape those. Everything else is already escaped.
+        $logmsg = str_replace('</', '<\/',
+            '<strong>' . $lg->rev . '</strong> ' .
+            '&lt;' . Chora::showAuthorName($lg->queryAuthor(), true) . '&gt; ' .
+            '<strong>' . sprintf(_("%s ago"), Chora::readableTime($lg->date, true)) . '</strong>' .
+            '<br />' .
+            str_replace(array("'", "\n"), array('&#039;', '\n'), $logmsg));
+        echo "revisionLogMap['" . $lg->rev . "'] = '$logmsg';\n";
+    }
+    ?>
+
+    var links = document.getElementsByTagName('A');
+    for (var i = 0; i < links.length; ++i) {
+        var rx = RegExp('^p?rev_([0-9.]+)_\\d+$').exec(links[i].id);
+        if (!rx) {
+            continue;
+        }
+        links[i].setAttribute('title', revisionLogMap[rx[1]]);
+    }
+
+    ToolTips.attachBehavior();
+}
+addEvent(window, 'load', setRevisionLogTitles);
+//]]>
+</script>
diff --git a/templates/annotate/header.inc b/templates/annotate/header.inc
index 382bd36..9036d2c 100644
--- a/templates/annotate/header.inc
+++ b/templates/annotate/header.inc
@@ -1,8 +1,11 @@
-<br />
-<table width="100%" cellspacing="0" cellpadding="2" border="0" class="annotate-back">
- <tr class="annotate-header">
-  <th align="right"><?php echo _("#") ?></th>
-  <th align="left"><?php echo _("Author") ?></th>
-  <th align="left"><?php echo _("Rev") ?></th>
-  <th align="left"><?php echo _("Line") ?></th>
+<table width="100%" cellspacing="0" class="annotate">
+<thead>
+ <tr>
+  <th class="rightAlign"><?php echo _("#") ?></th>
+  <th><?php echo _("Author") ?></th>
+  <th><?php echo _("Rev") ?></th>
+  <th><?php echo _("Prev") ?></th>
+  <th><?php echo _("Line") ?></th>
  </tr>
+</thead>
+<tbody>
diff --git a/templates/annotate/line.inc b/templates/annotate/line.inc
index 56464cd..795a775 100644
--- a/templates/annotate/line.inc
+++ b/templates/annotate/line.inc
@@ -1,14 +1,7 @@
- <tr>
-  <td align="right" class="item1">
-   <a id="l<?php echo $lineno ?>" href="#l<?php echo $lineno ?>"><?php echo $lineno ?></a>
-  </td>
-  <td class="annotate-author">
-   <?php echo $author ?>
-  </td>
-  <td class="annotate-rev">
-   <a href="<?php echo Chora::url('co', $where, array('r'=>$rev)) ?>"><?php echo $rev ?></a>
-  </td>
-  <td class="item<?php echo $style ?>">
-   <tt><?php echo (trim($line) != '') ? $line : '&nbsp;' ?></tt>
-  </td>
+ <tr id="l<?php echo $lineno ?>">
+  <td class="rightAlign"><?php echo $lineno ?></td>
+  <td class="author"><?php echo $author ?></td>
+  <td class="rev"><a id="rev_<?php echo $rev . '_' . $i ?>" href="<?php echo Chora::url('co', $where, array('r' => $rev)) ?>"><?php echo $rev ?></a></td>
+  <td class="rev"><?php if ($prev): ?><a id="prev_<?php echo $prev . '_' . $i ?>" href="<?php echo Chora::url('annotate', $where, array('rev' => $prev)) . '#l' . $lineno ?>"><?php echo $prev ?></a><?php else: ?>&nbsp;<?php endif; ?></td>
+  <td class="item<?php echo $style ?>"><tt><?php echo (trim($line) != '') ? $line : '&nbsp;' ?></tt></td>
  </tr>
diff --git a/templates/checkout/checkout.inc b/templates/checkout/checkout.inc
index b0fc19d..80bee06 100644
--- a/templates/checkout/checkout.inc
+++ b/templates/checkout/checkout.inc
@@ -1,65 +1,26 @@
-<table cellspacing="1" class="control" width="100%">
-<tr>
-  <td class="item<?php echo $i++ % 2 ?>">
-    <?php printf(_("Revision <b>%s</b>, <i>%s</i> (%s ago) by %s"), $r, $commitDate, $readableDate, $author) ?>
-  </td>
-</tr>
-
-<?php if (!empty($branchName)): ?>
-<tr>
-  <td class="item<?php echo $i++ % 2 ?>">
-    <?php echo _("Branch:") ?> <b><a href="<?php echo Chora::url('', $where, array('onb' => $branchRev)) ?>"><?php echo $branchName ?></a></b>
-  </td>
-</tr>
-<?php endif; ?>
-
-<?php if (!empty($commitTags)): ?>
-<tr>
-  <td class="item<?php echo $i++ % 2 ?>">
-    <?php echo _("Commit Tags:") ?> <b><?php echo $commitTags ?></b>
-  </td>
-</tr>
-<?php endif; ?>
-
-<?php if (!empty($branchPoints)): ?>
-<tr>
-  <td class="item<?php echo $i++ % 2 ?>">
-    <?php echo _("Branch Point for:") ?> <b><?php echo $branchPoints ?></b>
-  </td>
-</tr>
-<?php endif; ?>
-
-<?php if (!empty($prevRevision)): ?>
-<tr>
-  <td class="item<?php echo $i++ % 2 ?>">
-    <?php printf(_("Changed since <b>%s</b>"), $prevRevision) ?>: <?php echo $changedLines ?>
-  </td>
-</tr>
-<?php endif; ?>
-
-<tr valign="top">
-  <td class="control">
-    <b><?php echo _("Log:") ?></b>
- </td>
-</tr>
-<tr valign="top">
-  <td class="text">
-    <?php echo $log_print ?>
- </td>
-</tr>
-</table>
-
-<br />
-
-<table cellspacing="0" class="checkout" width="100%">
-<tr><td class="checkout">
-
+<h3 class="revision_log"><?php echo _("Log Message") ?></h3>
+<div class="revision_log">
+ <ul class="revision striped">
+  <?php if ($author): ?><li><?php echo _("Author:") ?> <?php echo $author ?></li><?php endif; ?>
+  <?php if ($branchName): ?><li><?php echo _("Branch:") ?> <strong><a href="<?php echo Chora::url('', $where, array('onb' => $branchRev)) ?>"><?php echo $branchName ?></a></strong></li><?php endif; ?>
+  <?php if ($tags): ?><li class="tags"><?php echo _("Tags:") ?> <?php echo implode(', ', $tags) ?></li><?php endif; ?>
+ </ul>
+ <?php echo $log_print ?>
+ <div class="clear"></div>
+</div>
+
+<h3 class="checkout"><?php echo _("Checkout") ?></h3>
+<div class="checkout">
 <?php
-if (strpos($pretty->getType(), 'text/plain') !== false) {
-    echo '<pre>' . htmlspecialchars($pretty->render()) . '</pre>';
-} else {
+$type = $pretty->getType();
+if (strpos($type, 'text/plain') !== false) {
+    echo '<div class="fixed">' . Text_Filter::filter($pretty->render(), 'text2html', array('parselevel' => TEXT_HTML_MICRO)) . '</div>';
+} elseif (strpos($type, 'image/') !== false) {
+    echo Horde::img(Util::addParameter(Horde::selfUrl(true), 'p', 1), '', '', '');
+} elseif ($pretty->canDisplayInline()) {
     echo $pretty->render();
+} else {
+    echo Horde::link(Util::addParameter(Horde::selfUrl(true), 'p', 1)) . Horde::img('download.png', '', '', $registry->getImageDir('horde')) . ' ' . sprintf(_("Download revision %s"), $r) . '</a>';
 }
 ?>
-
-</td></tr></table>
+</div>
diff --git a/templates/common-header.inc b/templates/common-header.inc
index 93fb2c1..56cfee5 100644
--- a/templates/common-header.inc
+++ b/templates/common-header.inc
@@ -5,9 +5,9 @@ if (isset($language)) {
 }
 ?>
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "DTD/xhtml1-transitional.dtd">
-<!--          Chora: Copyright 2000-2006, The Horde Project.           -->
-<!-- Horde Project: http://horde.org/ | Chora: http://horde.org/chora/ -->
-<!--     GNU Public License: http://www.fsf.org/copyleft/gpl.html      -->
+<!--  Chora: Copyright 2000-2009 The Horde Project.  Chora is under the GPL.   -->
+<!-- Horde Project: http://www.horde.org/ | Chora: http://www.horde.org/chora/ -->
+<!--        GNU Public License: http://www.fsf.org/copyleft/gpl.html           -->
 <?php echo !empty($language) ? '<html lang="' . strtr($language, '_', '-') . '">' : '<html>' ?>
 <head>
 <?php
@@ -18,10 +18,12 @@ if (!empty($refresh_time) && ($refresh_time > 0) && !empty($refresh_url)) {
     echo "<meta http-equiv=\"refresh\" content=\"$refresh_time;url=$refresh_url\">\n";
 }
 
+Horde::includeScriptFiles();
+
 ?>
 <title><?php echo htmlspecialchars($page_title) ?></title>
-<link href="<?php echo $GLOBALS['registry']->getImageDir()?>/favicon.ico" rel="SHORTCUT ICON" />
+<link href="<?php echo $GLOBALS['registry']->getImageDir() ?>/favicon.ico" rel="SHORTCUT ICON" />
 <?php echo Horde::stylesheetLink('chora') ?>
 </head>
 
-<body<?php if (Util::nonInputVar('bodyClass')) echo ' class="' . $bodyClass . '"' ?>>
+<body<?php if ($bc = Util::nonInputVar('bodyClass')) echo ' class="' . $bc . '"' ?><?php if ($bi = Util::nonInputVar('bodyId')) echo ' id="' . $bi . '"'; ?>>
diff --git a/templates/cvsgraph/cvsgraph.inc b/templates/cvsgraph/cvsgraph.inc
index aa2b5d7..e844190 100644
--- a/templates/cvsgraph/cvsgraph.inc
+++ b/templates/cvsgraph/cvsgraph.inc
@@ -1,14 +1,13 @@
-<?php echo $map ?>
-<table width="100%" cellspacing="0" cellpadding="1" border="0">
-<tr>
-  <td style="background-color:white;">
-    <img usemap="#graphMap" alt="<?php echo htmlspecialchars($title) ?>" src="<?php echo $imgUrl ?>" border="0" />
-  </td>
-</tr>
-<tr>
-  <td class="smallheader">
-    <?php echo _("Click on the revisions and branches to display the file.") ?><br />
-    <?php echo _("Click on the links in between revisions to get a diff between those revisions.") ?>
+<table class="options" cellspacing="0">
+ <tr>
+  <td>
+   <?php echo _("Click on the revisions and branches to display the file.") ?><br />
+   <?php echo _("Click on the links in between revisions to get a diff between those revisions.") ?>
   </td>
-</tr>
+ </tr>
 </table>
+
+<p class="history">
+ <img usemap="#graphMap" alt="<?php echo htmlspecialchars($title) ?>" src="<?php echo $imgUrl ?>" />
+</p>
+<?php echo $map ?>
diff --git a/templates/diff/hr/add.inc b/templates/diff/hr/add.inc
index f3b19ba..2a0908d 100644
--- a/templates/diff/hr/add.inc
+++ b/templates/diff/hr/add.inc
@@ -1,4 +1,4 @@
 <tr>
- <td class="hr-diff-grey">&nbsp;</td>
- <td class="hr-diff-add"><tt><?php echo !empty($line) ? $line : '&nbsp;' ?></tt></td>
+ <td class="added_empty">&nbsp;</td>
+ <td class="added"><pre><?php echo strlen($line) ? $line : '&nbsp;' ?></pre></td>
 </tr>
diff --git a/templates/diff/hr/change.inc b/templates/diff/hr/change.inc
index b473b66..abbd37a 100644
--- a/templates/diff/hr/change.inc
+++ b/templates/diff/hr/change.inc
@@ -1,20 +1,20 @@
 <tr>
 <?php if (!empty($left)): ?>
-  <td class="hr-diff-change">
-   <tt><?php echo $left ?></tt>
-  </td>
+ <td class="modified">
+  <pre><?php echo $left ?></pre>
+ </td>
 <?php elseif ($row < $oldsize): ?>
-  <td class="hr-diff-change">&nbsp;</td>
+ <td class="modified">&nbsp;</td>
 <?php else: ?>
-  <td class="hr-diff-nochange">&nbsp;</td>
+ <td class="unmodified">&nbsp;</td>
 <?php endif; ?>
 <?php if (!empty($right)): ?>
-  <td class="hr-diff-change">
-   <tt><?php echo $right ?></tt>
-  </td>
+ <td class="modified">
+  <pre><?php echo $right ?></pre>
+ </td>
 <?php elseif ($row < $newsize): ?>
-  <td class="hr-diff-change">&nbsp;</td>
+ <td class="modified">&nbsp;</td>
 <?php else: ?>
-  <td class="hr-diff-nochange">&nbsp;</td>
+ <td class="unmodified">&nbsp;</td>
 <?php endif; ?>
 </tr>
diff --git a/templates/diff/hr/empty.inc b/templates/diff/hr/empty.inc
index 8b53c88..99f1b9f 100644
--- a/templates/diff/hr/empty.inc
+++ b/templates/diff/hr/empty.inc
@@ -1,5 +1,5 @@
-<tr class="hr-diff-context">
- <td><tt><?php echo $line ?></tt></td>
- <td><tt><?php echo $line ?></tt></td>
+<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/templates/diff/hr/footer.inc b/templates/diff/hr/footer.inc
index a86a9ab..02ee59f 100644
--- a/templates/diff/hr/footer.inc
+++ b/templates/diff/hr/footer.inc
@@ -1,24 +1,2 @@
-</table>
-<br />
-<table border="0" cellspacing="0" cellpadding="0"> 
- <tr>
-   <th class="hr-diff-back" colspan="3"><?php echo _("Legend:") ?></th>
- </tr>
- <tr>
-   <td>
-   <table border="0" cellspacing="0" cellpadding="2">
-     <tr>
-      <td align="center" class="hr-diff-remove"><?php printf(_("Removed in v.%s"), $r1) ?></td>
-      <td class="hr-diff-grey">&nbsp;</td>
-     </tr>
-     <tr class="hr-diff-change">
-      <td align="center" colspan="2"><?php echo _("changed lines") ?></td>
-     </tr>
-     <tr>
-      <td class="hr-diff-grey">&nbsp;</td>
-      <td align="center" class="hr-diff-add"><?php printf(_("Added in v.%s"), $r2) ?></td>
-     </tr>
-   </table>
-  </td>
- </tr>
+</tbody>
 </table>
diff --git a/templates/diff/hr/header.inc b/templates/diff/hr/header.inc
index 1354343..a104d6c 100644
--- a/templates/diff/hr/header.inc
+++ b/templates/diff/hr/header.inc
@@ -1,15 +1,52 @@
-<br />
-<table border="0" cellpadding="2" cellspacing="0" class="checkout" width="100%">
-<tr>
- <td class="diff-log">
-  <b><?php printf(_("Last Log Message for rev %s:"), $r2) ?></b><br /><br />
-  <?php echo $log_print ?>
- </td>
-</tr>
+<table class="options" cellspacing="0">
+ <tr>
+  <td>
+   <form method="get" action="diff.php">
+    <?php echo Chora::formInputs() ?>
+    <input type="hidden" name="f" value="<?php echo htmlspecialchars(Util::getFormData('f')) ?>" />
+    <input type="hidden" name="r1" value="<?php echo htmlspecialchars(Util::getFormData('r1')) ?>" />
+    <input type="hidden" name="r2" value="<?php echo htmlspecialchars(Util::getFormData('r2')) ?>" />
+    <?php echo _("Download diff as: ") ?>
+    <select name="t" onchange="this.form.submit()">
+     <option value="unified"><?php echo _("Unified") ?></option>
+     <option value="context"><?php echo _("Context") ?></option>
+     <option value="column"><?php echo _("Column") ?></option>
+     <option value="ed"><?php echo _("Ed Script") ?></option>
+    </select>
+    <input class="button" type="submit" value="<?php echo _("Get Diff") ?>" /><br />
+   </form>
+  </td>
+ </tr>
 </table>
-<br />
-<table border="0" cellspacing="0" cellpadding="0" width="100%">
-<tr class="hr-diff-back">
- <th width="50%"><a href="<?php echo Chora::url('co', $where, array('r' => $r1)) ?>"><?php printf(_("version %s"), $r1) ?></a></th>
- <th width="50%"><a href="<?php echo Chora::url('co', $where, array('r' => $r2)) ?>"><?php printf(_("version %s"), $r2) ?></a></th>
+<table cellspacing="0" class="hrdiff">
+<caption>
+ <?php $blank = $registry->getImageDir('horde') . '/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>
+<?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['branchName'])): ?><li><?php echo _("Branch:") ?> <strong><a href="<?php echo Chora::url('', $where, array('onb' => $val['branchRev'])) ?>"><?php echo $val['branchName'] ?></a></strong></li><?php endif; ?>
+   <?php if (!empty($val['tags'])): ?><li class="tags"><?php echo _("Tags:") ?> <?php echo implode(', ', $val['tags']) ?></li><?php endif; ?>
+  </ul>
+  <a href="<?php echo Chora::url('co', $where, array('r' => $val['rev'])) ?>"><?php echo $val['rev'] ?></a>: <?php echo $val['msg'] ?>
+  <div class="diffclear"></div>
+ </div>
+<?php endforeach; ?>
+</div>
+<?php endif; ?>
+<thead>
+<tr>
+ <th><a href="<?php echo Chora::url('co', $where, array('r' => $r1)) ?>"><?php printf(_("Version %s"), $r1) ?></a></th>
+ <th><a href="<?php echo Chora::url('co', $where, array('r' => $r2)) ?>"><?php printf(_("Version %s"), $r2) ?></a></th>
 </tr>
+</thead>
+<tbody>
diff --git a/templates/diff/hr/nochange.inc b/templates/diff/hr/nochange.inc
index d604a7b..05b8186 100644
--- a/templates/diff/hr/nochange.inc
+++ b/templates/diff/hr/nochange.inc
@@ -1,8 +1,5 @@
 <tr>
-  <td colspan="2">&nbsp;</td>
-</tr>
-<tr class="hr-diff-grey">
-  <td colspan="2" align="center">
-    <b>- <?php echo _("No Viewable Change") ?> -</b>
-  </td>
+ <th>
+  <?php echo _("No Visible Changes") ?>
+ </th>
 </tr>
diff --git a/templates/diff/hr/remove.inc b/templates/diff/hr/remove.inc
index 8b992af..e6c020a 100644
--- a/templates/diff/hr/remove.inc
+++ b/templates/diff/hr/remove.inc
@@ -1,6 +1,4 @@
 <tr>
-  <td class="hr-diff-remove">
-    <tt><?php echo $line ?></tt>
-  </td>
-  <td class="hr-diff-grey">&nbsp;</td>
+ <td class="removed"><pre><?php echo strlen($line) ? $line : '&nbsp;' ?></pre></td>
+ <td class="removed_empty">&nbsp;</td>
 </tr>
diff --git a/templates/diff/hr/row.inc b/templates/diff/hr/row.inc
index 0acbc83..c115de3 100644
--- a/templates/diff/hr/row.inc
+++ b/templates/diff/hr/row.inc
@@ -1,10 +1,8 @@
-<tr class="hr-diff-linenum">
- <td width="50%">
-   &nbsp; <b><?php printf(_("Line %s"), $lefthead) ?> </b>
-   &nbsp;<?php echo $headfunc ?>
- </td>
- <td width="50%">
-   &nbsp; <b><?php printf(_("Line %s"), $righthead) ?> </b>
-   &nbsp;<?php echo $headfunc ?>
- </td>
+<tr>
+ <th>
+  <?php printf(_("Line %s"), $lefthead) ?>
+ </th>
+ <th>
+  <?php printf(_("Line %s"), $righthead) ?>
+ </th>
 </tr>
diff --git a/templates/directory/back.inc b/templates/directory/back.inc
index 54d7c5d..dbb1895 100644
--- a/templates/directory/back.inc
+++ b/templates/directory/back.inc
@@ -1,9 +1,11 @@
-<tr class="item0">
- <td>
-  <?php echo Horde::img('back.png', _("Back"), 'width="16" height="16"') ?> &nbsp;<a href="<?php echo $url ?>"><?php echo _("Parent Directory") ?></a>
- </td>
- <td> &nbsp; </td>
- <td> &nbsp; </td>
- <td> &nbsp; </td>
- <td> &nbsp; </td>
-</tr>
+<tbody>
+ <tr>
+  <td>
+   <?php echo Horde::img('back.png', _("Back"), 'width="16" height="16"') ?> <a href="<?php echo $url ?>"><?php echo _("Parent Directory") ?></a>
+  </td>
+  <td>&nbsp;</td>
+  <td>&nbsp;</td>
+  <td>&nbsp;</td>
+  <td>&nbsp;</td>
+ </tr>
+</tbody>
diff --git a/templates/directory/dir.inc b/templates/directory/dir.inc
index c36b6d7..627b4c6 100644
--- a/templates/directory/dir.inc
+++ b/templates/directory/dir.inc
@@ -1,13 +1,13 @@
-<tr class="item<?php echo $dirrow ?>">
- <td>
+ <tr>
+  <td>
 <?php if (!Chora::isRestricted("$where/$currentDir")): ?>
-   <?php echo Horde::img('folder.png', _("Directory"), 'width="16" height="16"') ?> &nbsp;<a href="<?php echo $url ?>"><?php echo $currDir ?>/</a>
+   <?php echo Horde::img('folder.png', _("Directory"), 'width="16" height="16"') ?> <a href="<?php echo $url ?>"><?php echo $currDir ?>/</a>
 <?php else: ?>
-   <?php echo Horde::img('folder.png', _("Directory"), 'width="16" height="16"') ?> &nbsp;<?php echo $currDir ?>/
+   <?php echo Horde::img('folder.png', _("Directory"), 'width="16" height="16"') ?> <?php echo $currDir ?>/
 <?php endif; ?>
- </td>
- <td> &nbsp; </td>
- <td> &nbsp; </td>
- <td> &nbsp; </td>
- <td> &nbsp; </td>
-</tr>
+  </td>
+  <td>&nbsp;</td>
+  <td>&nbsp;</td>
+  <td>&nbsp;</td>
+  <td>&nbsp;</td>
+ </tr>
diff --git a/templates/directory/file.inc b/templates/directory/file.inc
index 4dc6575..fb6728b 100644
--- a/templates/directory/file.inc
+++ b/templates/directory/file.inc
@@ -1,40 +1,41 @@
-<tr class="<?php echo $attic ? 'attic' : "item$dirrow" ?>">
+ <tr<?php if ($attic) echo ' class="attic"' ?>>
 <?php if (!Chora::isRestricted($fileName)): ?>
- <td nowrap="nowrap">
-<?php if ($attic) : ?>
-  <?php echo Horde::img('deleted.png', _("Deleted File"), 'width="16" height="16"') ?>&nbsp;
+  <td>
+<?php if ($attic): ?>
+   <?php echo Horde::img('deleted.png', _("Deleted File"), 'width="16" height="16"') ?>
 <?php else: ?>
-  <img src="<?php echo $icon ?>" border="0" width="16" height="16" alt="<?php echo _("File") ?>" />&nbsp;
+   <img src="<?php echo $icon ?>" width="16" height="16" alt="<?php echo _("File") ?>" />
 <?php endif; ?>
-  <a href="<?php echo $url ?>"><?php echo $name ?></a>
- </td>
- <td>
-   &nbsp;<b><a href="<?php echo Chora::url('co', $fileName, array('r' => $head)) ?>"><?php echo $head ?></a></b>
- </td>
+<?php if ($conf['filename_linkto'] == 'revlog'): ?>
+   <a href="<?php echo $url ?>">
 <?php else: ?>
- <td nowrap="nowrap">
-<?php if ($attic) : ?>
-   <?php echo Horde::img('deleted.png', _("Deleted File"), 'width="16" height="16"') ?>&nbsp;
+   <a href="<?php echo Chora::url('co', $fileName, array('r' => $head)) ?>">
+<?php endif; ?>
+<?php echo $name ?></a>
+  </td>
+  <td>
+   <strong>
+<?php if ($conf['filename_linkto'] == 'revlog'): ?>
+   <a href="<?php echo Chora::url('co', $fileName, array('r' => $head)) ?>">
+<?php else: ?>
+   <a href="<?php echo $url ?>">
+<?php endif; ?>
+<?php echo $head ?></a></strong>
+  </td>
+<?php else: ?>
+  <td>
+<?php if ($attic): ?>
+   <?php echo Horde::img('deleted.png', _("Deleted File"), 'width="16" height="16"') ?>
 <?php else: ?>
-   <img src="<?php echo $icon ?>" border="0" width="16" height="16" alt="<?php echo _("File") ?>" />&nbsp;
+   <img src="<?php echo $icon ?>" width="16" height="16" alt="<?php echo _("File") ?>" />
 <?php endif; ?>
    <?php echo $name ?>
- </td>
- <td>
-   &nbsp;<b><?php echo $head ?></b>
- </td>
+  </td>
+  <td><strong><?php echo $head ?></strong></td>
 <?php endif; ?>
- <td>
-   &nbsp;<?php echo $author ?>
- </td>
- <td nowrap="nowrap">
-   &nbsp;
-   <i><?php echo $readableDate ?></i>
- </td>
- <td nowrap="nowrap">
-   &nbsp;
-   <?php if (!empty($log)) : ?>
-   <?php echo htmlspecialchars($shortLog) ?>
-   <?php endif; ?>
- </td>
-</tr>
+  <td><?php echo $author ?></td>
+  <td sortval="<?php echo (int)$date ?>">
+   <em><?php echo $readableDate ?></em>
+  </td>
+  <td><?php if (!empty($log)) echo htmlspecialchars($shortLog) ?></td>
+ </tr>
diff --git a/templates/directory/footer.inc b/templates/directory/footer.inc
deleted file mode 100644
index 23c8198..0000000
--- a/templates/directory/footer.inc
+++ /dev/null
@@ -1,3 +0,0 @@
-</table>
-</td></tr>
-</table>
diff --git a/templates/directory/header.inc b/templates/directory/header.inc
index b8709d2..c41a7e1 100644
--- a/templates/directory/header.inc
+++ b/templates/directory/header.inc
@@ -1,26 +1,19 @@
-<table width="100%" border="0" cellspacing="0" cellpadding="0">
-<tr><td class="item">
-<table width="100%" border="0" cellspacing="1" cellpadding="2">
- <tr class="item">
-  <th class="<?php echo $acts['sbt'] == VC_SORT_NAME ? 'selected' : '' ?>" align="left">
-    <a class="widget" href="<?php echo $url['name'] ?>"><?php echo Horde::img($acts['ord'] ? 'za.png' :
-'az.png', _("Sort Order"), 'width="9" height="9"', $registry->getImageDir('horde')) ?></a>&nbsp;
-    <?php echo Horde::widget($url['name'], _("File"), 'widget', '', '', _("_File")) ?>
+<?php $sortdirclass = $acts['sbt'] ? 'sortdown' : 'sortup' ?>
+<table id="browse" cellspacing="0" class="browse sortable striped nowrap">
+<thead>
+ <tr>
+  <th id="s<?php echo VC_SORT_NAME ?>"<?php if ($acts['sbt'] == VC_SORT_NAME) echo ' class="' . $sortdirclass . '"' ?>>
+   <?php echo Horde::widget($url['name'], _("File"), 'sortlink', '', '', _("_File")) ?>
   </th>
-  <th class="<?php echo $acts['sbt'] == VC_SORT_REV ? 'selected' : '' ?>" align="left" nowrap="nowrap">
-    <a class="widget" href="<?php echo $url['rev'] ?>"><?php echo Horde::img($acts['ord'] ? 'za.png' :
-'az.png', _("Sort Order"), 'width="9" height="9"', $registry->getImageDir('horde')) ?></a>&nbsp;
-    <?php echo Horde::widget($url['rev'], _("Rev"), 'widget', '', '', _("_Rev")) ?>
+  <th id="s<?php echo VC_SORT_REV ?>"<?php if ($acts['sbt'] == VC_SORT_REV) echo ' class="' . $sortdirclass . '"' ?>>
+   <?php echo Horde::widget($url['rev'], _("Rev"), 'sortlink', '', '', _("_Rev")) ?>
   </th>
-  <th class="<?php echo $acts['sbt'] == VC_SORT_AUTHOR ? 'selected' : '' ?>" align="left" nowrap="nowrap">
-    <a class="widget" href="<?php echo $url['author'] ?>"><?php echo Horde::img($acts['ord'] ? 'za.png' :
-'az.png', _("Sort Order"), 'width="9" height="9"', $registry->getImageDir('horde')) ?></a>&nbsp;
-    <?php echo Horde::widget($url['author'], _("Author"), 'widget', '', '', _("_Author")) ?>
+  <th id="s<?php echo VC_SORT_AUTHOR ?>"<?php if ($acts['sbt'] == VC_SORT_AUTHOR) echo ' class="' . $sortdirclass . '"' ?>>
+   <?php echo Horde::widget($url['author'], _("Author"), 'sortlink', '', '', _("_Author")) ?>
   </th>
-  <th class="<?php echo $acts['sbt'] == VC_SORT_AGE ? 'selected' : '' ?>" align="left" nowrap="nowrap">
-    <a class="widget" href="<?php echo $url['age'] ?>"><?php echo Horde::img($acts['ord'] ? 'za.png' :
-'az.png', _("Sort Order"), 'width="9" height="9"', $registry->getImageDir('horde')) ?></a>&nbsp;
-    <?php echo Horde::widget($url['age'], _("Date"), 'widget', '', '', _("Da_te")) ?>
+  <th id="s<?php echo VC_SORT_AGE ?>"<?php if ($acts['sbt'] == VC_SORT_AGE) echo ' class="' . $sortdirclass . '"' ?>>
+   <?php echo Horde::widget($url['age'], _("Date"), 'sortlink', '', '', _("Da_te")) ?>
   </th>
-  <th align="left" class="widget"><?php echo _("Last Log") ?></th>
+  <th class="nosort"><?php echo _("Last Log") ?></th>
  </tr>
+</thead>
diff --git a/templates/error_page.inc b/templates/error_page.inc
index 42af74e..7d9c89f 100644
--- a/templates/error_page.inc
+++ b/templates/error_page.inc
@@ -6,12 +6,12 @@
 <body>
 <h2><?php echo _("Error Encountered") ?> - <?php echo $errcode ?></h2>
 <p />
-<b><?php echo $errmsg ?></b>
+<strong><?php echo $errmsg ?></strong>
 <p />
 <hr />
-<small><i>
+<small><em>
 <?php echo _("Please contact") ?> <a href="mailto:<?php echo $GLOBALS['conf']['options']['adminEmail'] ?>">
 <?php echo $conf['options']['adminName'] ?></a> <?php echo _("for further information.") ?>
-</i></small>
+</em></small>
 </body>
 </html>
diff --git a/templates/headerbar.inc b/templates/headerbar.inc
index d15e57a..01819ba 100644
--- a/templates/headerbar.inc
+++ b/templates/headerbar.inc
@@ -1,40 +1,23 @@
-<table width="100%" cellspacing="1" cellpadding="3" border="0">
-<tr>
-  <td class="header"><?php echo $title ?></td>
-</tr>
-
-<tr>
-  <td class="item">
-    <table width="100%" cellspacing="0" cellpadding="0" border="0">
-      <tr>
-        <td>
-          <?php echo _("Location:") ?>
-          <b>[ <a href="<?php echo Chora::url('') ?>"><?php echo $conf['options']['sourceRootName'] ?></a> ]
-          <?php echo Chora::whereMenu() ?></b>
-
-<?php if (isset($onb) && $onb): ?>
-          &nbsp; &nbsp; <i>(<?php echo _("Tracking Branch") ?> <b><?php echo $fl->branches[$onb] ?></b>)</i>
-<?php 
-endif;
-if ($where == '' && sizeof($sourceroots) > 1): 
-?>
-          &nbsp; &nbsp;
-          (<?php echo Chora::repositories() ?>)
-<?php endif; ?>
-        </td>
+<h1 class="header"><?php echo htmlspecialchars($title) ?></h1>
+<table cellspacing="0" class="headerbar" id="headerbar">
+ <tr>
+  <td>
+   <?php echo _("Location:") ?>
+   <strong>[ <a href="<?php echo Chora::url('') ?>"><?php echo $conf['options']['sourceRootName'] ?></a> ]
+   <?php echo Chora::whereMenu() ?></strong>
+   <?php if (isset($fl) && isset($onb) && isset($fl->branches[$onb])): ?>
+    <em>(<?php echo _("Tracking Branch") ?> <strong><?php echo $fl->branches[$onb] ?></strong>)</em>
+   <?php endif; ?>
+  </td>
 <?php if (!empty($extraLink)): ?>
-        <td align="right"><?php echo $extraLink ?></td>
+  <td class="rightAlign"><?php echo $extraLink ?></td>
 <?php endif; ?>
-      </tr>
-    </table>
-  </td>
-</tr>
+ </tr>
 
-<?php if ($where == '' && @is_file($conf['paths']['introText'])): ?>
-<tr class="text">
-  <td><?php @readfile($conf['paths']['introText']) ?></td>
-</tr>
+<?php if (!$where && @is_file($conf['paths']['introText'])): ?>
+ <tr>
+  <td class="text" colspan="2"><?php @readfile($conf['paths']['introText']) ?></td>
+ </tr>
 <?php endif; ?>
 
 </table>
-<br />
diff --git a/templates/history/blank.inc b/templates/history/blank.inc
index 12de619..a5b0e25 100644
--- a/templates/history/blank.inc
+++ b/templates/history/blank.inc
@@ -1 +1 @@
-<td <?php echo $bg?"style=\"background-color: $bg\"":'' ?>> &nbsp; </td>
+<td<?php echo $bg ? " style=\"background:$bg\"":'' ?>>&nbsp;</td>
\ No newline at end of file
diff --git a/templates/history/branch_cell.inc b/templates/history/branch_cell.inc
index bd4a4b6..0f955f2 100644
--- a/templates/history/branch_cell.inc
+++ b/templates/history/branch_cell.inc
@@ -1,7 +1,7 @@
-<td style="background-color: <?php echo $bg ?>" nowrap="nowrap" align="center">
+<td style="background:<?php echo $bg ?>; text-align:center">
 <?php echo _("Branching to") ?>:<br />
 <a href="<?php echo Chora::url('', $where, array('onb' => $rev)); ?>">
- <span class="title"><?php echo $symname ?></span></a>
+ <?php echo $symname ?></a>
 <br />
-<i>(<?php printf(_("revision %s"), $rev) ?>)</i>
+<em>(<?php printf(_("revision %s"), $rev) ?>)</em>
 </td>
diff --git a/templates/history/header.inc b/templates/history/header.inc
index c5efc2d..74d28c6 100644
--- a/templates/history/header.inc
+++ b/templates/history/header.inc
@@ -1,2 +1 @@
-<br />
-<table cellspacing="4" cellpadding="3" border="0">
+<table cellspacing="5" class="history">
diff --git a/templates/history/rev.inc b/templates/history/rev.inc
index b4c2417..22118d8 100644
--- a/templates/history/rev.inc
+++ b/templates/history/rev.inc
@@ -1,9 +1,8 @@
-<td style="background-color: <?php echo $bg ?>" nowrap="nowrap">
- <a id="rev<?php echo $rev ?>" />
- <a href="<?php echo Chora::url('',$where, array('r'=>$rev), "rev$rev") ?>">
- <span class="title"><?php echo $rev ?></span></a> <?php printf(_("by %s"), $author) ?>
+<td id="rev<?php echo $rev ?>" style="background:<?php echo $bg ?>">
+ <a href="<?php echo Chora::url('', $where, array('r'=>$rev), "rev$rev") ?>">
+ <?php echo $rev ?></a> <?php printf(_("by %s"), $author) ?>
  <br />
- <i><?php echo $date ?></i>
+ <em><?php echo $date ?></em>
 <?php if (!empty($lines)): ?>
  <br /><?php printf(_("Changed: %s"), $lines) ?>
 <?php endif ?>
diff --git a/templates/history/row_end.inc b/templates/history/row_end.inc
deleted file mode 100644
index 2301952..0000000
--- a/templates/history/row_end.inc
+++ /dev/null
@@ -1 +0,0 @@
-</tr>
diff --git a/templates/history/row_start.inc b/templates/history/row_start.inc
deleted file mode 100644
index edf802e..0000000
--- a/templates/history/row_start.inc
+++ /dev/null
@@ -1 +0,0 @@
-<tr>
diff --git a/templates/log/footer.inc b/templates/log/footer.inc
new file mode 100644
index 0000000..a4d3642
--- /dev/null
+++ b/templates/log/footer.inc
@@ -0,0 +1,13 @@
+</tbody>
+</table>
+
+<?php if (count($fl->logs) > 100 && !Util::getFormData('all')): ?>
+<table class="options" cellspacing="0">
+ <tr>
+  <td>
+   <?php printf(_("Only showing the 100 latest revisions. %sShow all revisions?</a> (may take a while)."),
+                Horde::link(Util::addParameter(Horde::selfUrl(true), 'all', 1))) ?>
+  </td>
+ </tr>
+</table>
+<?php endif ?>
diff --git a/templates/log/header.inc b/templates/log/header.inc
new file mode 100644
index 0000000..67d3968
--- /dev/null
+++ b/templates/log/header.inc
@@ -0,0 +1,53 @@
+<table class="options" cellspacing="0">
+ <tr>
+  <td>
+   <?php echo _("Search Revisions:") ?>
+   <input type="text" for="revlog_body" size="20" />
+  </td>
+
+  <td class="spacer"></td>
+  <td>
+   <form method="get" action="diff.php">
+    <?php echo Chora::formInputs() ?>
+    <input type="hidden" name="f" value="<?php echo htmlspecialchars(Util::getFormData('f')) ?>" />
+    <?php printf(_("Show diffs between %s and %s"),
+                 '<input type="text" size="6" name="r1" value="' . $diffValueLeft . '" />',
+                 '<input type="text" size="6" name="r2" value="' . $diffValueRight . '" />') ?>
+    <input class="button" type="submit" value="<?php echo _("Diff") ?>" /><br />
+    <em><?php printf(_("%1\$s shows diffs to the previous revision. If you select a revision by clicking its row, %1\$s will show the differences to the selected row."),
+                     Horde::img('diff.png', _("Show Differences"))) ?></em>
+   </form>
+  </td>
+
+  <td class="spacer"></td>
+
+<?php if (!empty($selAllBranches)): ?>
+  <td>
+   <form method="get" action="browse.php">
+    <?php echo Chora::formInputs() ?>
+    <input type="hidden" name="f" value="<?php echo htmlspecialchars(Util::getFormData('f')) ?>" />
+    <?php echo _("Show Branch:") ?>
+    <select name="onb" onchange="this.form.submit()">
+     <option value="0"<?php if (!isset($onb) || !$onb) echo ' selected="selected"' ?>><?php echo _("All Branches") ?></option>
+     <?php echo $selAllBranches ?>
+    </select>
+    <noscript><input class="button" type="submit" value="<?php echo _("View") ?>" /></noscript>
+   </form>
+  </td>
+<?php else: ?>
+   <td>&nbsp;</td>
+<?php endif; ?>
+ </tr>
+</table>
+
+<table class="revlog striped sortable" id="revlog" cellspacing="0">
+<thead>
+ <tr class="item leftAlign">
+  <th class="nosort">&nbsp;</th>
+  <th><?php echo _("Revision") ?></th>
+  <th class="sortup"><?php echo _("Date") ?></th>
+  <th><?php echo _("Author") ?></th>
+  <th class="nosort"><?php echo _("Log Message") ?></th>
+ </tr>
+</thead>
+<tbody id="revlog_body">
diff --git a/templates/log/request.inc b/templates/log/request.inc
deleted file mode 100644
index fb3d5f9..0000000
--- a/templates/log/request.inc
+++ /dev/null
@@ -1,66 +0,0 @@
-<a id="diff"></a>
-<br />
-<table width="100%" cellspacing="0" cellpadding="4">
-<tr><td class="control">
-<?php echo _("This form allows you to request diffs between any two revisions of a file.  You may select a symbolic revision name using the selection box or you may type in a numeric name using the type-in text box.") ?>
-</td></tr>
-<tr class="item"><td>
-<form method="get" action="<?php echo Chora::url('diff',$where) ?>">
-<?php echo Chora::generateHiddens() ?>
-<input type="hidden" name="f" value="<?php echo Util::getFormData('f') ?>" />
-<table cellspacing="0" cellpadding="1" border="0">
-<tr>
-<td align="right">
-<?php echo _("Retrieve diffs between:") ?>
-</td>
-<td>
-<select name="r1">
-  <option value="0" selected="selected"><?php echo _("Use Text Field") ?></option>
-  <?php echo $sel ?>
-</select>
-<input type="text" size="12" name="tr1" value="<?php echo $diffValueLeft ?>" />
-</td>
-<td><?php echo _("Type:") ?></td>
-<td>
-<select name="ty">
- <option value="h" selected="selected"><?php echo _("Human Readable") ?></option>
- <option value="u"><?php echo _("Unified") ?></option>
- <option value="c"><?php echo _("Context") ?></option>
- <option value="s"><?php echo _("Side-by-Side") ?></option>
- <option value="e"><?php echo _("Ed Script") ?></option>
-</select> &nbsp;
-</td>
-</tr>
-<tr>
-<td align="right"><?php echo _("and:") ?></td>
-<td>
-<select name="r2">
-  <option value="0" selected="selected"><?php echo _("Use Text Field") ?></option>
-  <?php echo $sel ?>
-</select>
-<input type="text" size="12" name="tr2" value="<?php echo $diffValueRight ?>" />
-</td>
-<td>&nbsp;</td>
-<td><input class="button" type="submit" value="<?php echo _("Get Diffs") ?>" /></td>
-</tr>
-</table>
-</form>
-</td></tr>
-<?php if (!empty($selAllBranches)): ?>
-<tr class="item"><td>
-<form method="get" action="<?php echo Chora::url('', $where) ?>">
-<?php echo Chora::generateHiddens() ?>
-<table cellspacing="0" cellpadding="1" border="0">
-<tr><td><?php echo _("View revisions on:") ?></td>
-<td>
-<select name="onb">
-  <option value="0" <?php if (!isset($onb) || !$onb) echo ' selected="selected"' ?>><?php echo _("All Branches") ?></option>
-  <?php echo $selAllBranches ?>
-</select> &nbsp;
-<input class="button" type="submit" value="<?php echo _("View Branch") ?>" />
-</td></tr>
-</table>
-</form>
-</td></tr>
-<?php endif; ?>
-</table>
diff --git a/templates/log/rev.inc b/templates/log/rev.inc
index 43bab7e..f9c605a 100644
--- a/templates/log/rev.inc
+++ b/templates/log/rev.inc
@@ -1,66 +1,17 @@
-<a id="rev<?php echo $rev ?>" />
-<table width="100%" cellspacing="0" class="item">
-<tr>
-  <td align="left" class="<?php echo $bgclass ?>">
-    <a href="<?php echo $textURL ?>" class="title"><?php echo $rev ?></a> <?php printf(_("by %s"), $author) ?>
-  </td>
-  <td align="right" class="<?php echo $bgclass ?>">
-    <a href="<?php echo Chora::url('annotate', $where, array('rev' => $rev)); ?>"><?php echo _("Annotate") ?></a>
-  | <a href="<?php echo Chora::url('co', $where, array('r' => $rev)); ?>"><?php echo _("View") ?></a>
-  | <a href="<?php echo Chora::url('co', $where, array('r' => $rev, 'p' => 1)); ?>"><?php echo _("Download") ?></a>
-<?php if ($manyRevisions): ?>
-<?php if ($r1 === $rev): ?>
-| <strong><a href="<?php echo Chora::url('', $where) ?>"><?php echo _("Deselect") ?></a></strong>
-<?php else: ?>
-| <a href="<?php echo $selURL ?>"><?php echo _("Select for Diff") ?></a>
-<?php endif; ?>
-<?php endif; ?>
-  </td>
-</tr>
-<tr>
-  <td colspan="2">
-    <table width="100%" cellspacing="0">
-      <tr>
-        <td width="35%" valign="top" class="box">
+<tr id="rev<?php echo $rev ?>">
+ <td>
+  <?php if ($diffUrl): ?>
+  <a href="<?php echo $diffUrl ?>" class="pdiff" title="<?php echo _("Show changes to the previous revision") ?>"><?php echo Horde::img('diff.png') ?></a>
+  <?php endif ?>
+  <a href="<?php echo Chora::url('diff', $where, array('r1' => 0, 'r2' => $rev)) ?>" class="sdiff" title="<?php echo _("Show changes to the selected revision") ?>" onclick="revlog_sdiff(this);"><?php echo Horde::img('diff.png') ?></a>
+ </td>
+ <td><a href="<?php echo $textUrl ?>"><?php echo $rev . '</a>'; if ($branchName) echo ' <span class="branch">' . Horde::link(Chora::url('', $where, array('onb' => $branchRev))) . htmlspecialchars($branchName) . '</a></span>'; if (!empty($lg->lines)) echo ' <small>(' . sprintf('%s lines', $lg->lines) . ')</small>'; ?></td>
+ <td class="ago" sortval="<?php echo (int)$lg->date ?>"><a title="<?php echo $readableDate ?>"><?php echo $commitDate ?></a></td>
+ <td class="author"><?php echo $author ?></td>
+ <td class="log">
 <?php
-echo $commitDate . ' <em>(' . sprintf(_("%s ago"), $readableDate) . ')</em>';
-if (!empty($branchName)) {
-    echo '<br />' . _("Branch:") . ' <strong>' . Horde::link(Chora::url('', $where, array('onb' => $branchRev)), $branchName) . $branchName . '</a></strong>';
-}
-if (!empty($branchPoints)) {
-    echo '<br />' . _("Branch Point for:") . ' <strong>' . $branchPoints . '</strong>';
-}
-if (!empty($commitTags)) {
-    echo '<br />' . _("Tags:") . ' <strong>' . $commitTags . '</strong>';
-}
-if (!empty($prevRevision)) {
-    echo '<br />' . sprintf(_("Changed since <strong>%s</strong>"), $prevRevision) . ': ' . $changedLines;
-}
-if (!empty($prevRevision)) { ?>
-<br />
-<a href="<?php echo $uniDiffURL ?>"><?php printf(_("Diffs to version %s"), $prevRevision) ?></a>
-(<a href="<?php echo $coloredDiffURL ?>"><?php echo _("Colored") ?></a> |
-<a href="<?php echo $longDiffURL ?>"><?php echo _("Long") ?></a> |
-<a href="<?php echo $nowsDiffURL ?>"><?php echo _("NoWhitespaceChanges") ?></a>)
-<?php }
-
-if ($manyRevisions && !empty($r1) && ($r1 != $prevRevision) && ($r1 != $rev)) { ?>
-<br />
-<a href="<?php echo $selUniDiffURL ?>"><?php printf(_("Diffs to <strong>%s</strong>"), $r1) ?></a>
-(<a href="<?php echo $selColoredDiffURL ?>"><?php echo _("Colored") ?></a> |
-<a href="<?php echo $selLongDiffURL ?>"><?php echo _("Long") ?></a> |
-<a href="<?php echo $selNowsDiffURL ?>"><?php echo _("NoWhitespaceChanges") ?></a>)
-<?php } ?>
-          </td>
-          <td></td>
-          <td width="65%" valign="top" class="box">
-            <?php echo $logMessage ?>
-          </td>
-        </tr>
-      </table>
-    </td>
-  </tr>
-  <tr>
-    <td colspan="2" style="font-size:5px;">&nbsp;</td>
-  </tr>
-</table>
+echo '<p class="message">' . $logMessage . '</p>';
+if ($tags) echo '<p class="tags">' . _("Tags") . ': ' . implode(', ', $tags) . '</p>';
+?>
+ </td>
+</tr>
diff --git a/templates/menu.inc b/templates/menu.inc
index 6a0bbd2..ebda88b 100644
--- a/templates/menu.inc
+++ b/templates/menu.inc
@@ -1,5 +1,7 @@
 <div id="menu">
+ <div class="rightFloat">
+  <?php echo Chora::repositories() ?>
+ </div>
  <?php echo Chora::getMenu('string') ?>
 </div>
-<br id="menuSpacer" />
 <?php $GLOBALS['notification']->notify(array('listeners' => 'status')) ?>
diff --git a/templates/patchsets/footer.inc b/templates/patchsets/footer.inc
new file mode 100644
index 0000000..02ee59f
--- /dev/null
+++ b/templates/patchsets/footer.inc
@@ -0,0 +1,2 @@
+</tbody>
+</table>
diff --git a/templates/patchsets/header.inc b/templates/patchsets/header.inc
new file mode 100644
index 0000000..a040d0a
--- /dev/null
+++ b/templates/patchsets/header.inc
@@ -0,0 +1,20 @@
+<table class="options" cellspacing="0">
+ <tr>
+  <td>
+   <?php echo _("Search Patchsets:") ?>
+   <input type="text" for="patchsets_body" size="20" />
+  </td>
+ </tr>
+</table>
+
+<table class="revlog striped sortable" id="patchsets" cellspacing="0">
+<thead>
+ <tr class="item leftAlign">
+  <th><?php echo _("Patchset") ?></th>
+  <th class="sortup"><?php echo _("Date") ?></th>
+  <th><?php echo _("Author") ?></th>
+  <th class="nosort"><?php echo _("Files") ?></th>
+  <th class="nosort"><?php echo _("Log Message") ?></th>
+ </tr>
+</thead>
+<tbody id="patchsets_body">
diff --git a/templates/patchsets/ps.inc b/templates/patchsets/ps.inc
index 02ee700..c2543b2 100644
--- a/templates/patchsets/ps.inc
+++ b/templates/patchsets/ps.inc
@@ -1,33 +1,25 @@
-<a id="ps<?php echo $id ?>" />
-<table width="100%" cellspacing="0" cellpadding="3" class="diff-back">
-<tr class="diff-back"><td width="100%"> 
-<table width="100%" cellspacing="0" cellpadding="4" class="diff-back">
-
-<tr class="item"><td align="left"><span class="title"><?php printf(_("PatchSet %s</span> by %s"), $id, $author) ?></td><td align="right">
-<?php echo $allDiffsLink; ?></a>
-</td></tr>
-
-<tr valign="top" class="diff-back">
-<td width="35%">
-<?php echo $commitDate ?> <i>(<?php printf(_("%s ago"), $readableDate) ?>)</i><br />
-<?php if (!empty($patchset['branch'])): ?>
-<?php echo _("Branch:") ?> <b><?php echo htmlspecialchars($patchset['branch']) ?></b>
-<?php endif; ?>
-<?php if (!empty($patchset['tags'])): ?>
-<?php echo _("Commit Tags:") ?> <b><?php echo htmlspecialchars($patchset['tags']) ?></b>
-<?php endif; ?>
-
-<br />
-<?php if (count($files)): ?><br /><b><?php echo _("Files Changed:") ?></b><table border="0" width="100%" cellpadding="2" cellspacing="0">
-<?php $i = 0; foreach ($files as $file): ?>
-<tr><td class="item<?php echo $i++ % 2 ?>"><?php echo $file['file'] . ': ' . $file['from'] . ' -> ' . $file['to'] . ' ' . $file['diff'] ?></td></tr>
-<?php endforeach; ?>
-</table>
-<?php endif; ?>
-
-</td><td class="diff-log" width="65%">
-<?php echo $logMessage ?>
-</td></tr>
-</table>
-</td></tr>
-</table>
+<tr id="ps<?php echo $id ?>">
+ <td class="pset"><?php echo $patchset_link ?></td>
+ <td class="ago" sortval="<?php echo (int)$patchset['date'] ?>"><a title="<?php echo $readableDate ?>"><?php echo $commitDate ?></a></td>
+ <td class="author"><?php echo $author ?></td>
+ <td class="files">
+  <ul>
+   <?php foreach ($files as $file): ?>
+   <li><?php echo $file['file'] . ': ' . $file['from'] . ' -> ' . $file['to'] . ' ' . $file['diff'] ?>
+   <?php endforeach; ?>
+  </ul>
+ </td>
+ <td class="log">
+<?php
+echo '<p class="message">' . $logMessage . '</p>';
+$tags = array();
+if (isset($patchset['branch'])) {
+    $tags[] = htmlspecialchars($patchset['branch']);
+}
+if (isset($patchset['tag'])) {
+    $tags[] = htmlspecialchars($patchset['tag']);
+}
+if ($tags) echo '<p class="tags">' . _("Tags") . ': ' . implode(', ', $tags) . '</p>';
+?>
+ </td>
+</tr>
diff --git a/templates/stats/stats.inc b/templates/stats/stats.inc
index 9fc9711..1aadea0 100644
--- a/templates/stats/stats.inc
+++ b/templates/stats/stats.inc
@@ -1,22 +1,20 @@
-<table border="0" cellpadding="3" cellspacing="0" width="100%">
-<tr>
-  <th class="control" align="right"><?php echo _("Author") ?>&nbsp;&nbsp;</th>
-  <th align="left" class="control"><?php echo _("Commits") ?></th>
-</tr>
+<table class="stats striped" cellspacing="0">
+<thead>
+ <tr>
+  <th class="rightAlign"><?php echo _("Author") ?></th>
+  <th><?php echo _("Commits") ?></th>
+ </tr>
+</thead>
+<tbody>
 <?php
 
 $most = max($author_stats);
 $factor = $most / 400;
-$i = 0;
-foreach ($author_stats as $author => $commits) {
-    $width = (int)($commits / $factor);
-    $i = ($i + 1) % 2;
-?>
-<tr>
-  <td class="item<?php echo $i ?>" align="right" valign="top"><?php echo $author ?>&nbsp;&nbsp;</td>
-  <td class="item<?php echo $i ?>" valign="top"><?php echo Horde::img('pixel.php?c=bbcbff', '', 'style="border:1px solid black;" width="' . $width . '" height="10"', $registry->get('webroot', 'horde') . '/services/images') . '&nbsp;&nbsp;(' . $commits . ')</td>' ?>
-</tr>
-
-<?php } ?>
-
+foreach ($author_stats as $author => $commits): $width = (int)($commits / $factor); ?>
+ <tr>
+  <td class="rightAlign"><?php echo $author ?>&nbsp;&nbsp;</td>
+  <td><?php echo Horde::img('pixel.php?c=bbcbff', '', 'style="border:1px solid #000;" width="' . $width . '" height="10"', $registry->get('webroot', 'horde') . '/services/images') . ' (' . $commits . ')</td>' ?>
+ </tr>
+<?php endforeach ?>
+</tbody>
 </table>
diff --git a/test.php b/test.php
new file mode 100644
index 0000000..ca20d78
--- /dev/null
+++ b/test.php
@@ -0,0 +1,89 @@
+<?php
+/**
+ * $Horde: chora/test.php,v 1.18.2.2 2009/01/06 15:22:34 jan Exp $
+ *
+ * Copyright 2006-2009 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 Michael Slusarz <slusarz at horde.org>
+ */
+
+/* Include Horde's core.php file. */
+include_once '../lib/core.php';
+
+/* We should have loaded the String class, from the Horde_Util
+   package, in core.php. If String:: isn't defined, then we're not
+   finding some critical libraries. */
+if (!class_exists('String')) {
+    echo '<br /><h2 style="color:red">The Horde_Util package was not found. If PHP\'s error_reporting setting is high enough and display_errors is on, there should be error messages printed above that may help you in debugging the problem. If you are simply missing these files, then you need to get the <a href="http://cvs.horde.org/cvs.php/framework">framework</a> module from <a href="http://www.horde.org/source/">Horde CVS</a>, and install the packages in it with the install-packages.php script.</h2>';
+    exit;
+}
+
+/* Initialize the Horde_Test:: class. */
+if (!is_readable('../lib/Test.php')) {
+    echo 'ERROR: You must install Horde before running this script.';
+    exit;
+}
+require_once '../lib/Test.php';
+$horde_test = new Horde_Test;
+
+/* Chora version. */
+$module = 'Chora';
+require_once './lib/version.php';
+$module_version = CHORA_VERSION;
+
+/* Chora configuration files. */
+$file_list = array(
+    'config/conf.php' => 'The file <code>./config/conf.php</code> appears to be missing. You probably just forgot to generate it using the Horde config system - see docs/INSTALL for details. While you do that, take a look at the settings and make sure they are appropriate for your site.',
+    'config/mime_drivers.php' => 'The file <code>./config/mime_drivers.php</code> appears to be missing. You probably just forgot to copy <code>./config/mime_drivers.php.dist</code> over. While you do that, take a look at the settings and make sure they are appropriate for your site.',
+    'config/prefs.php' => 'The file <code>./config/prefs.php</code> appears to be missing. You probably just forgot to copy <code>./config/prefs.php.dist</code> over. While you do that, take a look at the settings and make sure they are appropriate for your site.',
+    'config/sourceroots.php' => 'The file <code>./config/sourceroots.php</code> appears to be missing. You probably just forgot to copy <code>./config/sourceroots.php.dist</code> over. While you do that, take a look at the settings and make sure they are appropriate for your site.'
+);
+
+require TEST_TEMPLATES . 'header.inc';
+require TEST_TEMPLATES . 'version.inc';
+
+/* PHP module capabilities. */
+$module_list = array();
+
+/* Display versions of other Horde applications. */
+$app_list = array(
+    'horde' => array(
+        'error' => 'Chora requires Horde 3.0 or greater to operate.',
+        'version' => '3.0'
+    ),
+);
+
+?>
+<h1>Other Horde Applications</h1>
+<ul>
+    <?php echo $horde_test->requiredAppCheck($app_list) ?>
+</ul>
+<?php
+
+/* Display PHP Version information. */
+$php_info = $horde_test->getPhpVersionInformation();
+require TEST_TEMPLATES . 'php_version.inc';
+
+/* PEAR */
+$pear_list = array();
+
+/* Get the status output now. */
+$module_output = $horde_test->phpModuleCheck($module_list);
+
+?>
+
+<h1>Chora Configuration Files</h1>
+<ul>
+    <?php echo $horde_test->requiredFileCheck($file_list) ?>
+</ul>
+
+<h1>PEAR Modules</h1>
+<ul>
+    <?php echo $horde_test->PEARModuleCheck($pear_list) ?>
+</ul>
+
+<?php
+require TEST_TEMPLATES . 'footer.inc';
diff --git a/themes/burntorange/screen.css b/themes/burntorange/screen.css
index 5ec30a9..8915cc4 100644
--- a/themes/burntorange/screen.css
+++ b/themes/burntorange/screen.css
@@ -1,5 +1,5 @@
 /**
- * $Horde: chora/themes/burntorange/screen.css,v 1.1 2004/10/17 20:37:45 mdjukic Exp $
+ * $Horde: chora/themes/burntorange/screen.css,v 1.1.10.1 2008/10/09 17:40:34 jan Exp $
  */
 
 /* Normally we use burnt orange for the select background.  But here, since we
diff --git a/themes/graphics/az.png b/themes/graphics/az.png
new file mode 100644
index 0000000..2b646f9
Binary files /dev/null and b/themes/graphics/az.png differ
diff --git a/themes/graphics/back.png b/themes/graphics/back.png
index 8dc3346..6ee2d98 100644
Binary files a/themes/graphics/back.png and b/themes/graphics/back.png differ
diff --git a/themes/graphics/chora.png b/themes/graphics/chora.png
index 34d0cd3..70a18b8 100644
Binary files a/themes/graphics/chora.png and b/themes/graphics/chora.png differ
diff --git a/themes/graphics/deleted.png b/themes/graphics/deleted.png
index d2767b3..502b4b1 100644
Binary files a/themes/graphics/deleted.png and b/themes/graphics/deleted.png differ
diff --git a/themes/graphics/diff.png b/themes/graphics/diff.png
new file mode 100644
index 0000000..04fab07
Binary files /dev/null and b/themes/graphics/diff.png differ
diff --git a/themes/graphics/folder.png b/themes/graphics/folder.png
index c7c3ccc..323c51b 100644
Binary files a/themes/graphics/folder.png and b/themes/graphics/folder.png differ
diff --git a/themes/graphics/za.png b/themes/graphics/za.png
new file mode 100644
index 0000000..a154237
Binary files /dev/null and b/themes/graphics/za.png differ
diff --git a/themes/hordeweb/screen.css b/themes/hordeweb/screen.css
new file mode 100644
index 0000000..911af8e
--- /dev/null
+++ b/themes/hordeweb/screen.css
@@ -0,0 +1,48 @@
+/**
+ * $Horde: chora/themes/hordeweb/screen.css,v 1.2.2.1 2008/10/09 17:40:35 jan Exp $
+ */
+
+table.options, table.revlog, table.headerbar, table.browse, table.stats, table.annotate {
+    width: 100%;
+    margin-left: 0;
+}
+table.options td {
+    font-size: 100%;
+}
+table.revlog th, table.browse th, table.stats th, table.annotate th {
+    background: #cfc;
+}
+table.headerbar {
+    background: #cfc;
+    border: none;
+    padding-left: 5px;
+    padding-right: 5px;
+    -moz-border-radius: 8px;
+    -webkit-border-radius: 8px;
+}
+table.headerbar tr {
+    background: transparent;
+}
+table.headerbar td {
+    border: none;
+}
+table.headerbar td.text {
+    border-bottom: 5px solid #cfc;
+}
+
+div.revision_log, h3.revision_log, h3.checkout, #revlog tr.hover, #revlog tr:hover, div.checkout, table.hrdiff, table.history, p.history {
+    margin-left: 0;
+    margin-right: 0;
+    border-color: #cfc;
+}
+h3.revision_log, h3.checkout {
+    background: #cfc;
+    color: #063;
+    font-size: 100%;
+    padding-left: 5px;
+    padding-right: 5px;
+    -moz-border-radius-topright: 8px;
+    -moz-border-radius-topleft: 8px;
+    -webkit-border-top-right-radius: 8px;
+    -webkit-border-top-left-radius: 8px;
+}
diff --git a/themes/screen.css b/themes/screen.css
index 43e69b1..84bdce1 100644
--- a/themes/screen.css
+++ b/themes/screen.css
@@ -1,74 +1,289 @@
 /**
- * $Horde: chora/themes/screen.css,v 1.1 2004/11/25 02:49:39 chuck Exp $
+ * $Horde: chora/themes/screen.css,v 1.1.4.1 2008/10/09 17:40:34 jan Exp $
  */
 
-.title {
-    font-size: 15px;
+/* Menu bottom margin, added for BC. */
+#menu {
+    margin-bottom: 8px;
+}
+
+/* Image alignment, added for BC. */
+img {
+    vertical-align: middle;
+}
+
+/* Table CSS, added for BC. */
+th {
+    color: #333;
+    border-bottom: 1px solid #999;
+}
+
+/* Sort arrow styles, added for BC. */
+.sortup {
+    background: #bbcbff url("graphics/za.png") center left no-repeat;
+    padding-left: 10px;
+}
+.sortdown {
+    background: #bbcbff url("graphics/az.png") center left no-repeat;
+    padding-left: 10px;
+}
+
+.QuickFinderNoMatch {
+    display: none;
+}
+
+form#repository-picker {
+    display: inline;
+}
+
+/* Options boxes. */
+table.options {
+    width: 99%;
+    margin: 0 0 8px 5px;
+}
+table.options td {
+    font-size: 80%;
+    width: 33%;
+    border: 1px solid #ccc;
+    padding: 2px;
+    background: #e9e9e9;
+    vertical-align: top;
+}
+table.options td.spacer {
+    width: 10px;
+    background: none;
+    border: none;
+}
+table.options em {
+    display: block;
+    margin-top: 4px;
+}
+
+/* Tables. */
+table.revlog, table.headerbar, table.browse, table.stats {
+    width: 99%;
+    margin: 0 0 8px 5px;
+    border-top: 1px solid #ddd;
+    border-left: 1px solid #ddd;
+}
+table.headerbar {
+    border-right: 1px solid #ddd;
+}
+table.revlog th, table.browse th, table.stats th, table.annotate th {
+    padding: 3px;
+    background: #e9e9e9;
+    border-right: 1px solid #ccc;
+    text-align: left;
+}
+table.stats th.rightAlign {
+    text-align: right;
+}
+table.revlog td, table.headerbar td, table.browse td, table.stats td {
+    padding: 3px;
+    border-right: 1px solid #ddd;
+    border-bottom: 1px solid #ddd;
+}
+table.headerbar tr {
+    background: #e9e9e9;
+}
+table.headerbar td {
+    border-right: none;
+}
+table.revlog th.sortup, table.headerbar th.sortup, table.browse th.sortup {
+    background: #bbcbff url("graphics/za.png") center left no-repeat;
+    padding-left: 10px;
+}
+table.revlog th.sortdown, table.headerbar th.sortdown, table.browse th.sortdown {
+    background: #bbcbff url("graphics/az.png") center left no-repeat;
+    padding-left: 10px;
+}
+
+/* History View. */
+table.history, p.history {
+    margin: 0 0 8px 5px;
+    border: 1px solid #ddd;
+}
+table.history a {
     font-weight: bold;
+    font-size: 120%;
 }
-.outline-lite {
-    background-color: #cdcdcd;
+
+/* Individual revision information. */
+div.revision_log {
+    margin: 2px 5px 8px 5px;
+    border: 1px solid #ddd;
+    padding: 3px;
+}
+ul.revision {
+    padding: 2px;
+    float: right;
+    width: 50%;
+    list-style: none;
 }
-.outline-menu {
-    background-color: #9999cc;
+ul.revision li.tags {
+    font-style: italic;
 }
+
+/* Labels. */
+h3.revision_log, h3.checkout {
+    background: #e9e9e9;
+    display: inline;
+    font-weight: bold;
+    margin: 0 0 0 5px;
+    font-size: 90%;
+    padding: 2px;
+    border: 1px solid #ddd;
+}
+
+/* Revision logs. */
+#revlog tr.hover, #revlog tr:hover {
+    cursor: pointer;
+    background: #bbcbff;
+}
+.revlog tr.selected *, .revlog tr.selected td.ago a  {
+    background: #ffc;
+}
+.revlog td {
+    vertical-align: top;
+}
+.revlog td.log {
+    width: 60%;
+}
+.revlog td.ago a {
+    color: #000;
+    text-decoration: none;
+}
+.revlog .branchpoint {
+    margin-bottom: 4px;
+}
+.revlog .branch {
+    font-style: italic;
+}
+.revlog .message {
+    margin-bottom: 0;
+}
+.revlog .tags {
+    font-style: italic;
+    margin-top: 4px;
+    margin-bottom: 0;
+}
+.revlog .sdiff {
+    display: none;
+}
+.revlog .selection .sdiff {
+    display: inline;
+}
+.revlog .selection .selected .sdiff {
+    display: none;
+}
+.revlog .selection .pdiff {
+    display: none;
+}
+.revlog .selection .selected .pdiff {
+    display: inline;
+}
+
+/* Patchset logs. */
+#patchsets ul {
+    list-style: none;
+}
+#patchsets td.pset {
+    width: 1%;
+}
+#patchsets td.files, #patchsets td.log {
+    width: 35%;
+}
+
+/* Checkout. */
+div.checkout {
+    padding: 3px;
+    margin: 2px 5px 8px 5px;
+    border: 1px solid #ddd;
+}
+
 .attic {
-    background-color: #eedddd;
+    background: #edd;
     font-style: italic;
 }
-.diff-selected {
-    color: black;
-    border-bottom: 1px solid #999999;
-    background-color: #e0daaa;
+
+/* Annotate styles. */
+table.annotate {
+    width: 99%;
+    margin: 0 0 8px 5px;
+    border-top: 1px solid #ddd;
+    border-left: 1px solid #ddd;
+    border-bottom: 1px solid #ddd;
+}
+table.annotate th.rightAlign {
+    text-align: right;
 }
-.diff-back {
-    background-color: #ffffff;
+table.annotate td {
+    border-right: 1px solid #ddd;
 }
-.diff-log {
-    background-color: #eeeeee;
+table.annotate .author {
+    background: #eef;
 }
-.annotate-back {
-    background-color: #ffffff;
+table.annotate .rev {
+    background: #fee;
 }
-.annotate-header {
-    background-color: #dddddd;
+
+/* Diff styles. */
+table.hrdiff {
+    width: 99%;
+    margin: 0 0 8px 5px;
 }
-.annotate-author {
-    background-color: #eeeeff;
+table.hrdiff th {
+    text-align: left;
+    width: 50%;
 }
-.annotate-rev {
-    background-color: #ffeeee;
+table.hrdiff thead th {
+    font-weight: bold;
+    font-size: 110%;
+    padding: 2px;
 }
-.hr-diff-back {
-    background-color: #ffffff;
+table.hrdiff tbody th {
+    padding: 2em 1px 1px 1px;
+    font-size: 80%;
 }
-.hr-diff-add {
-    background-color: #ccccff;
+table.hrdiff .unmodified {
+    background: #fff;
 }
-.hr-diff-change {
-    background-color: #99ff99;
+table.hrdiff .added {
+    background: #9f9;
 }
-.hr-diff-nochange {
-    background-color: #99cc99;
+table.hrdiff .added_empty {
+    background: #cfc;
 }
-.hr-diff-remove {
-    background-color: #ff9999;
+table.hrdiff .modified {
+    background: #fd9;
 }
-.hr-diff-grey {
-    background-color: #cccccc;
+table.hrdiff .removed {
+    background: #f99;
 }
-.hr-diff-context {
-    background-color: #eeeeee;
+table.hrdiff .removed_empty {
+    background: #fcc;
 }
-.hr-diff-linenum {
-    background-color: #99cccc;
+table.hrdiff caption {
+    caption-side: top;
+    text-align: left;
+    margin: 0 0 8px 5px;
+    font-size: 90%;
+    font-weight: bold;
+    padding: 5px;
 }
-.history {
-    background-color: #f2f3ff;
+table.hrdiff caption img {
+    height: 10px;
+    width: 10px;
+    border: 1px solid #000;
+    margin-left: 0.5em;
+    vertical-align: baseline;
 }
-.history-branch {
-    background-color: #cceebb;
+
+div.difflog {
+    padding: 2px;
+    margin: 5px;
+    border: 1px solid #ddd;
 }
-.checkout {
-    background-color: #fafafa;
+div.diffclear {
+    clear: both;
 }
diff --git a/themes/silver/graphics/az.png b/themes/silver/graphics/az.png
new file mode 100644
index 0000000..24df0f4
Binary files /dev/null and b/themes/silver/graphics/az.png differ
diff --git a/themes/silver/graphics/back.png b/themes/silver/graphics/back.png
new file mode 100644
index 0000000..fdc394c
Binary files /dev/null and b/themes/silver/graphics/back.png differ
diff --git a/themes/silver/graphics/chora.png b/themes/silver/graphics/chora.png
new file mode 100644
index 0000000..597262b
Binary files /dev/null and b/themes/silver/graphics/chora.png differ
diff --git a/themes/silver/graphics/deleted.png b/themes/silver/graphics/deleted.png
new file mode 100644
index 0000000..ebad933
Binary files /dev/null and b/themes/silver/graphics/deleted.png differ
diff --git a/themes/silver/graphics/diff.png b/themes/silver/graphics/diff.png
new file mode 100644
index 0000000..04fab07
Binary files /dev/null and b/themes/silver/graphics/diff.png differ
diff --git a/themes/silver/graphics/favicon.ico b/themes/silver/graphics/favicon.ico
new file mode 100644
index 0000000..461907c
Binary files /dev/null and b/themes/silver/graphics/favicon.ico differ
diff --git a/themes/silver/graphics/folder.png b/themes/silver/graphics/folder.png
new file mode 100644
index 0000000..784e8fa
Binary files /dev/null and b/themes/silver/graphics/folder.png differ
diff --git a/themes/silver/graphics/za.png b/themes/silver/graphics/za.png
new file mode 100644
index 0000000..9b23c06
Binary files /dev/null and b/themes/silver/graphics/za.png differ
diff --git a/themes/silver/themed_graphics b/themes/silver/themed_graphics
new file mode 100644
index 0000000..e69de29

-- 
Debian Horde Packages repository: chora2 package



More information about the pkg-horde-hackers mailing list