Commits

Olemis Lang  committed d883538

BH Theme #162 : Rebase patches for #162 against r1433074

  • Participants
  • Parent commits 3459851
  • Branches t162_bootstrap_vcs

Comments (0)

Files changed (4)

+t162/t162_r1433074_bootstrap_revlog.diff
+t162/t162_r1433074_bootstrap_browser.diff
+t162/t162_r1433074_bootstrap_revgraph.diff

File t162/t162_r1433074_bootstrap_browser.diff

+# HG changeset patch
+# Parent d3aa761ceacca70389fbd904f13bb66e3aef47ec
+BH Theme #162 : Convert repository browser web UI to Bootstrap styling
+
+diff -r d3aa761ceacc bloodhound_theme/bhtheme/htdocs/css/browser.css
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/bloodhound_theme/bhtheme/htdocs/css/browser.css	Wed Jan 16 01:51:30 2013 -0500
+@@ -0,0 +1,209 @@
++#prefs { margin-top: -0.6em }
++* html #prefs { width: 34em } /* Set width only for IE */
++#prefs fieldset { margin: 0; }
++#prefs fieldset label { display: block }
++#prefs .buttons { margin-top: -2.3em }
++#prefs .choice { 
++ float: left; 
++ margin: 0 .6em 0 .3em;
++ border-right: 1px dotted #d7d7d7;
++}
++
++#file-legend { margin-top: 3em; }
++
++/* Browser */
++#diffrev, #jumprev, #jumploc { float: right; font-size: 10px; margin: 0 0 0.6em 2em; }
++#diffrev form, #jumprev form, #jumploc form { margin: 0 }
++#diffrev input, #jumprev input, #jumploc select, #jumploc input { 
++  font-size: 10px; 
++  margin: 0; 
++}
++#jumploc div.buttons { margin: 0; }
++
++/* Browser file annotations */
++table.code th.blame { width: 5em; }
++table.code th.blame a { color: #ddd; }
++
++div.message { 
++  background: #f7f7f0;
++  border: 3px double #d7d7d7;
++  margin: 0;
++  padding: 8px; 
++  /* Note: border width and padding must be compensated for in the placement */
++}
++
++div.message div.inlinebuttons { float: right;  }
++
++/* Styles for the directory entries table
++   (extends the styles for "table.listing") */
++table.dirlist { margin-top: 0 }
++table.dirlist td.rev, table.dirlist td.age, table.dirlist td.author, table.dirlist td.change {
++ color: #888;
++ white-space: nowrap;
++ vertical-align: middle;
++}
++table.dirlist td.rev {
++ font-family: monospace;
++ letter-spacing: -0.08em;
++ font-size: 90%;
++ text-align: right;
++}
++table.dirlist td.size {  
++ color: #888;
++ white-space: nowrap;
++ text-align: right;
++ vertical-align: middle;
++ font-size: 70%;
++}
++table.dirlist td.size a.trac-rawlink, table.dirlist td.size a.trac-ziplink {
++ margin-left: .3em;
++}
++table.dirlist td.age {
++ border-width: 0 2px 0 0;
++ border-style: solid;
++ font-size: 85%;
++}
++table.dirlist td.name { width: 100%; white-space: nowrap }
++table.dirlist td.name a.parent {
++  background: url(../../common/parent.png) 0 50% no-repeat;
++  padding-left: 20px;
++}
++table.dirlist tr span.expander { 
++  background: url(../../common/expander_normal.png) 0 50% no-repeat; 
++  cursor: pointer; 
++  padding-left: 8px; 
++  margin-left: 4px; 
++}
++table.dirlist tr span.expander:hover { 
++  background: url(../../common/expander_normal_hover.png) 0 50% no-repeat;
++}
++table.dirlist tr.expanded span.expander { 
++  background: url(../../common/expander_open.png) 0 50% no-repeat; 
++  padding-left: 12px; 
++  margin-left: 0; 
++}
++table.dirlist tr.expanded span.expander:hover { 
++  background: url(../../common/expander_open_hover.png) 0 50% no-repeat; 
++}
++table.dirlist td.name a.dir {
++  background: url(../../common/folder.png) 0 50% no-repeat;
++  padding-left: 20px;
++}
++table.dirlist td.name a.file {
++  background: url(../../common/file.png) 0 50% no-repeat;
++  padding-left: 20px;
++}
++table.dirlist td.name a, table.dirlist td.rev a { border-bottom: none }
++table.dirlist td.author, table.dirlist td.change { font-size: 85% }
++table.dirlist td.rev a.chgset { 
++  background: url(../../common/changeset.png) 100% 50% no-repeat;
++  padding: 0 0 0 5px; 
++  margin: 0 5px 0 0; 
++}
++table.dirlist td.description { padding-left: 2em }
++table.dirlist td.description > :first-child { margin-top: 0 }
++table.dirlist td.description > :last-child { margin-bottom: 0 }
++table.dirlist td span.loading { 
++  background: url(../../common/loading.gif) 0 50% no-repeat;
++  padding-left: 20px;
++  font-style: italic 
++}
++
++#content.browser div.description { padding: 0 0.5em }
++
++/* Style for the ''View Changes'' button and the diff preparation form */
++#anydiff { margin-top: 1em; }
++#anydiff form, #anydiff div, #anydiff h2 { display: inline }
++#anydiff form th { text-align: right }
++#anydiff input {  vertical-align: baseline; margin: 0 -0.5em 0 1em }
++@media print {
++ #anydiff form { display:  none }
++}
++
++/* Log */
++tr.diff input { padding: 0 1em; margin: 0 }
++table.trac-graph tbody tr { height: 2em; }
++table.trac-graph tbody tr td.trac-graph { padding: 0; background-color: #fcfcfc; }
++table.trac-graph th.trac-graph, table.trac-graph td.trac-graph { display: none; }
++table.trac-graph td.summary { white-space: nowrap; }
++
++@media print { 
++ th.diff, td.diff { display: none }
++}
++
++/* Styles for the revision log table (extends the styles for "table.listing") */
++table.chglist { margin-top: 0 }
++.chglist td.diff, .chglist td.rev, .chglist td.age, 
++.chglist td.author, .chglist td.change {
++ white-space: nowrap;
++ vertical-align: middle;
++}
++.chglist td.author { color: #888 }
++.chglist td.change span.edit { 
++ border: 1px solid #999;
++ float: left;
++ margin: .2em .5em 0 0;
++ width: .8em; height: .8em;
++}
++.chglist td.diff { padding: 1px }
++.chglist td.change .comment { display: none }
++.chglist td.age { font-size: 85% }
++.chglist td.author { font-size: 85% }
++.chglist td.rev { 
++ font-family: monospace;  
++ letter-spacing: -0.08em;
++ font-size: 90%;
++ text-align: right; 
++}
++.chglist td.rev a { border-bottom: none }
++.chglist td.rev a.chgset {
++  background-repeat: no-repeat;
++  background-image: url(../../common/changeset.png); 
++  background-position: 100% 50%;
++  padding: 0 0 0 5px; 
++  margin: 0 5px 0 0; 
++}
++.chglist td.summary, .chglist td.log { 
++ width: 100%; 
++ font-size: 85%; 
++ vertical-align: middle; 
++}
++.chglist td.summary *, .chglist td.log * { margin-top: 0 }
++/* verbose mode */
++.chglist tr.verbose { border-top: none }
++.chglist tr.verbose td.filler, .chglist tr.verbose td.log {
++ border: none;
++ border-bottom: 1px solid #ddd;
++ color: #333;
++}
++.chglist tr.verbose td { border: none; }
++.chglist tr.verbose td.diff, .chglist tr.verbose td.filler {
++ border-left: 1px solid #ddd; 
++}
++.chglist tr.verbose td.summary, .chglist tr.verbose td.log {
++ border-right: 1px solid #ddd; 
++}
++
++#paging { margin: 1em 0 }
++
++/* Styles for the revision info in the file view (see also trac.css) */
++#info { margin: 0; }
++#info .props {
++ color: #666;
++ list-style: square;
++ margin: 0 0 .4em 1.6em;
++ padding: 0;
++}
++#info .props > li { padding: 2px 0; overflow: auto; }
++.trac-toggledeleted {
++ display: none;
++ margin-left: 3em;
++ white-space: nowrap;
++}
++
++/* Styles for the HTML preview */
++#preview { background: #fff; clear: both; margin: 0 }
++#preview .code-block { border-top: 1px solid #999; margin: 0 }
++#preview .image-file { overflow: hidden }
++#preview .image-file img { max-width: 100% }
++
+diff -r d3aa761ceacc bloodhound_theme/bhtheme/templates/bh_browser.html
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/bloodhound_theme/bhtheme/templates/bh_browser.html	Wed Jan 16 01:51:30 2013 -0500
+@@ -0,0 +1,244 @@
++<!DOCTYPE html
++    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
++    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
++<html xmlns="http://www.w3.org/1999/xhtml"
++    xmlns:py="http://genshi.edgewall.org/"
++    xmlns:xi="http://www.w3.org/2001/XInclude"
++    xmlns:i18n="http://genshi.edgewall.org/i18n">
++  <xi:include href="layout.html" />
++  <head>
++    <title py:choose="len(path_links)">
++      <py:when test="1">/</py:when>
++      <py:when test="2">${path_links[-1].name}</py:when>
++      <py:otherwise><i18n:msg params="basename, dirname">${path_links[-1].name} in ${'/'.join(part.name for part in path_links[1:-1])}</i18n:msg></py:otherwise>
++    </title>
++    <meta py:if="file and file.annotate" name="ROBOTS" content="NOINDEX, NOFOLLOW" />
++    <meta py:if="dir" name="ROBOTS" content="NOINDEX" />
++    <script type="text/javascript" src="${chrome.htdocs_location}js/folding.js"></script>
++    <script type="text/javascript">
++      jQuery(document).ready(function($) {
++        $(".trac-toggledeleted").show().click(function() {
++                  $(this).siblings().find(".trac-deleted").toggle();
++                  return false;
++        }).click();
++        $("#jumploc input").hide();
++        $("#jumploc select").change(function () {
++          this.parentNode.parentNode.submit();
++        });
++
++        <py:if test="dir or repo">
++          /* browsers using old WebKits have issues with expandDir... */
++          var webkit_rev = /AppleWebKit\/(\d+)/.exec(navigator.userAgent);
++          if ( !webkit_rev || (521 - webkit_rev[1]).toString()[0] == "-" )
++            enableExpandDir(null, $("table.dirlist tr"), {
++                action: 'inplace',
++                range_min_secs: '$dir.range_min_secs',
++                range_max_secs: '$dir.range_max_secs'
++            });
++        </py:if>
++        <py:if test="file">
++          <py:if test="file.annotate == 'blame'">
++            enableBlame("${href.changeset()}/", "${reponame}", "${path}");
++          </py:if>
++          $('#preview table.code').enableCollapsibleColumns($('#preview table.code thead th.content'));
++        </py:if>
++      });
++    </script>
++  </head>
++
++  <body>
++    <div id="content" class="browser row">
++
++      <py:if test="dir or file">
++        <div class="span12">
++          <h1 py:with="repo_index=repo and repo.repositories;
++                       path_links=None if repo_index else path_links">
++            <span py:if="repo_index" class="pathentry">Default Repository</span>
++            <py:if test="path_links">
++              <span py:choose="" py:with="part = path_links[-1]">
++                <py:when test="dir">Directory</py:when>
++                <py:when test="file">File</py:when>
++                <a class="path_entry" href="${part.href}" 
++                    title="${_('View %(name)s', name=part.name)}">${part.name}</a>
++              </span>
++            </py:if>
++          </h1>
++  
++          <div id="diffrev" class="pull-right">
++            <form action="${href.changeset()}" method="get" class="form-inline">
++              <div>
++                <label title="Show the diff against a specific revision"
++                       py:with="full_path = '/'.join(p.strip('/') for p in (reponame, path) if p)">
++                  View diff against: 
++                  <input type="text" name="old" size="6" class="input-small"/>
++                  <input type="hidden" name="old_path" value="$full_path"/>
++                  <input type="hidden" name="new" value="$stickyrev"/>
++                  <input type="hidden" name="new_path" value="$full_path"/>
++                </label>
++              </div>
++            </form>
++          </div>
++          <div id="jumprev" class="pull-right">
++            <form action="" method="get" class="form-inline">
++              <div>
++                <label for="rev" title="${_('Hint: clear the field to view latest revision') if stickyrev else None}">
++                  View revision:</label>
++                <input type="text" id="rev" name="rev" value="$stickyrev" size="6" class="input-small"/>
++              </div>
++            </form>
++          </div>
++  
++          <div py:if="quickjump_entries" id="jumploc" class="pull-right">
++            <form action="" method="get" class="form-inline">
++              <div class="buttons">
++                <label for="preselected">Visit:</label>
++                <select id="preselected" name="preselected" class="input-small">
++                  <option selected="selected" />
++                  <optgroup py:for="category, locations in groupby(quickjump_entries, key=lambda q: q[0])"
++                    label="${category}">
++                    <option py:for="_, name, path, rev in locations" value="${href.browser(reponame, path, rev=rev)}">$name</option>
++                  </optgroup>
++                </select>
++                <input type="submit" value="${_('Go!')}" title="Jump to the chosen preselected path" />
++              </div>
++            </form>
++          </div>
++        </div>
++
++        <div class="trac-tags span12" py:with="changeset = repos.get_changeset(repos.normalize_rev(stickyrev))">
++          <p>
++            <py:for each="branch, head in changeset.get_branches()">
++              <span py:if="branch not in ('default', 'master')"
++                  class="label label-inverse branch${' head' if head else ''}"
++                  title="${_('Branch head') if head else _('Branch')}">${branch}</span>
++            </py:for>
++            <py:for each="tag in changeset.get_tags()">&nbsp;
++              <span class="label tag" title="Tag">${tag}</span>
++            </py:for>
++          </p>
++        </div>
++      </py:if>
++
++      <div py:if="dir" class="span12">
++        <table class="listing dirlist" id="dirlist">
++          <xi:include href="bh_dirlist_thead.html" />
++          <tbody>
++            <py:if test="'up' in chrome.links">
++              <tr class="even">
++                <td class="name" colspan="6">
++                  <a class="parent" title="Parent Directory" href="${chrome.links.up[0].href}">../</a>
++                </td>
++              </tr>
++            </py:if>
++            <xi:include href="bh_dir_entries.html" />
++            <tr py:if="'up' not in chrome.links and not dir.entries" class="even">
++              <td class="name" colspan="6">
++                No files found
++              </td>
++            </tr>
++          </tbody>
++        </table>
++      </div>
++
++      <div class="span12">
++        <div py:if="properties or file" id="info" summary="Revision info">
++          <py:if test="file">
++            <strong py:with="cset = href.changeset(created_rev, reponame);
++                         rcset = href.changeset(created_rev, reponame, created_path);
++                         drev = display_rev(created_rev);
++                         author = authorinfo(file.changeset.author);
++                         age = pretty_dateinfo(file.changeset.date)">
++              <py:choose>
++                <py:when test="stickyrev">
++                  <i18n:msg params="stickyrev, rev, author, age">
++                    <a href="$rcset" title="View differences">Last change</a>
++                    on this file since ${display_rev(stickyrev)} was
++                    <a href="$cset" title="${_('View changeset %(rev)s', rev=drev)}">$drev</a>,
++                    checked in by $author, $age
++                  </i18n:msg>
++                </py:when>
++                <py:otherwise>
++                  <i18n:msg params="rev, author, age">
++                    <a href="$rcset" title="View differences">Last change</a>
++                    on this file was
++                    <a href="$cset" title="${_('View changeset %(rev)s', rev=drev)}">$drev</a>,
++                    checked in by $author, $age
++                  </i18n:msg>
++                </py:otherwise>
++              </py:choose>
++            </strong><br/>
++          </py:if>
++          <py:if test="file">
++            <py:choose>
++              <py:when test="wiki_format_messages" xml:space="preserve">
++                ${wiki_to_html(context.child('changeset', file.changeset.rev, parent=repos.resource),
++                               file.changeset.message, escape_newlines=True)}
++              </py:when>
++              <py:otherwise>${file.changeset.message}<br/></py:otherwise>
++            </py:choose>
++          </py:if>
++          <py:if test="properties">
++            <ul class="props">
++              <py:def function="prop_value(prop)">
++                <py:choose>
++                  <py:when test="istext(prop.value)"><em><code>$prop.value</code></em></py:when>
++                  <py:otherwise>$prop.value</py:otherwise>
++                </py:choose>
++              </py:def>
++              <li py:for="prop in properties" py:choose="">
++                <py:when test="prop.rendered">
++                   <span py:if="prop.rendered.name"
++                         py:attrs="prop.rendered.name_attributes" py:content="prop.rendered.name" />
++                   <div py:attrs="prop.rendered.content_attributes" py:content="prop.rendered.content" />
++                </py:when>
++                <py:otherwise>
++                  <i18n:msg params="name, value">Property <strong>$prop.name</strong> set to ${prop_value(prop)}</i18n:msg>
++                </py:otherwise>
++              </li>
++            </ul>
++          </py:if>
++          <py:if test="file" i18n:msg="size">
++            <span class="label label-info">File size:</span>
++            <span title="${_('%(size)s bytes', size=file.size)}">${pretty_size(file.size)}</span>
++          </py:if>
++        </div>
++        <br/>
++      </div>
++
++      <div py:if="dir and path == '/'" class="description">
++        ${wiki_to_html(context.child('source', '/', parent=repos.resource), repoinfo.description)}
++      </div>
++
++      <py:if test="repo and repo.repositories">
++        <hr py:if="dir"/>
++        <h2>Repository Index</h2>
++        <py:with vars="repoindex = 'repoindex'">
++          <xi:include href="bh_repository_index.html" />
++        </py:with>
++      </py:if>
++
++      <div py:if="file and file.preview" id="preview" class="searchable span12">
++        <xi:include href="preview_file.html" py:with="preview = file.preview"/>
++        <br/>
++      </div>
++
++      <div id="anydiff" class="span12">
++        <form action="${href.diff()}" method="get">
++          <div class="buttons">
++            <input type="hidden" name="new_path" value="${'/' + pathjoin(reponame, path)}" />
++            <input type="hidden" name="old_path" value="${'/' + pathjoin(reponame, path)}" />
++            <input type="hidden" name="new_rev" value="$stickyrev" />
++            <input type="hidden" name="old_rev" value="$stickyrev" />
++            <input type="submit" value="${_('View changes...')}" title="Select paths and revs for Diff" class="btn" />
++          </div>
++        </form>
++      </div>
++
++      <div id="help" i18n:msg="" class="help-block pull-right">
++        <span class="label label-info">Note</span> See <a href="${href.wiki('TracBrowser')}">TracBrowser</a>
++        for help on using the repository browser.
++      </div>
++
++    </div>
++  </body>
++</html>
+diff -r d3aa761ceacc bloodhound_theme/bhtheme/templates/bh_dir_entries.html
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/bloodhound_theme/bhtheme/templates/bh_dir_entries.html	Wed Jan 16 01:51:30 2013 -0500
+@@ -0,0 +1,39 @@
++<!--! Template for generating rows corresponding to directory entries -->
++<html xmlns="http://www.w3.org/1999/xhtml"
++      xmlns:py="http://genshi.edgewall.org/"
++      xmlns:xi="http://www.w3.org/2001/XInclude" py:strip="">
++  <py:for each="idx, entry in enumerate(dir.entries)"
++          py:with="change = dir.changes[entry.created_rev];
++                   chgset_context = change and context.child('changeset', change.rev, parent=repos.resource);
++                   chgset_view = change and change.is_viewable(perm);
++                   isdir = entry.kind == 'dir'">
++    <tr class="${'odd' if idx % 2 else 'even'}">
++      <td class="name">
++        <a class="$entry.kind" title="${_('View Directory') if isdir else _('View File')}"
++           href="${href.browser(reponame, entry.path, rev=stickyrev, 
++                                order=order if order != 'name' else None, desc=desc)}">$entry.name</a>
++      </td>
++      <td class="size">
++        <span title="${_('%(size)s bytes', size=entry.content_length)}">${pretty_size(entry.content_length)}</span>
++        <a py:if="entry.raw_href" href="$entry.raw_href" class="${'trac-ziplink' if isdir else 'trac-rawlink'}"                       
++           title="${_('Download as Zip archive') if isdir else _('Download')}">&#8203;<i class="icon-download-alt"></i></a>
++      </td>
++      <td class="rev">
++        <a title="View Revision Log" href="${href.log(reponame, entry.path, rev=rev)}">${display_rev(entry.created_rev)}</a>
++        <a title="View Changeset" class="chgset" href="${href.changeset(change.rev, reponame)}">&nbsp;</a>
++      </td>
++      <td class="age" style="${chgset_view and dir.timerange and 'border-color: rgb(%s,%s,%s)' %
++                               dir.colorize_age(dir.timerange.relative(change.date)) or None}">
++        ${pretty_dateinfo(change.date, dateonly=True) if chgset_view else '&ndash;'}
++      </td>
++      <td class="author">${authorinfo_short(change.author) if chgset_view else '&ndash;'}</td>
++      <td class="change" py:choose="">
++        <py:when test="chgset_view" py:choose="">
++          <py:when test="wiki_format_messages">${wiki_to_oneliner(chgset_context, change.message, shorten=True)}</py:when>
++          <py:otherwise>${shorten_line(change.message)}</py:otherwise>
++        </py:when>
++        <py:otherwise>&ndash;</py:otherwise>
++      </td>
++    </tr>
++  </py:for>
++</html>
+diff -r d3aa761ceacc bloodhound_theme/bhtheme/templates/bh_dirlist_thead.html
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/bloodhound_theme/bhtheme/templates/bh_dirlist_thead.html	Wed Jan 16 01:51:30 2013 -0500
+@@ -0,0 +1,15 @@
++<!--! Template snippet for a standard table header for a dirlist -->
++<html xmlns="http://www.w3.org/1999/xhtml"
++    xmlns:py="http://genshi.edgewall.org/"
++    xmlns:xi="http://www.w3.org/2001/XInclude" py:strip="">
++  <thead>
++    <tr>
++      <xi:include href="bh_sortable_th.html" py:with="class_ = 'name'; title = _('Name')"/>
++      <xi:include href="bh_sortable_th.html" py:with="class_ = 'size'; title = _('Size')"/>
++      <th class="rev">Rev</th>
++      <xi:include href="bh_sortable_th.html" py:with="class_ = 'date'; title = _('Age')"/>
++      <xi:include href="bh_sortable_th.html" py:with="class_ = 'author'; title = _('Author')"/>
++      <th class="change">Last Change</th>
++    </tr>
++  </thead>
++</html>
+diff -r d3aa761ceacc bloodhound_theme/bhtheme/templates/bh_repository_index.html
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/bloodhound_theme/bhtheme/templates/bh_repository_index.html	Wed Jan 16 01:51:30 2013 -0500
+@@ -0,0 +1,50 @@
++<!--! Template snippet for a table of repositories -->
++<html xmlns="http://www.w3.org/1999/xhtml"
++    xmlns:py="http://genshi.edgewall.org/"
++    xmlns:xi="http://www.w3.org/2001/XInclude" py:strip="">
++  <table class="listing dirlist" id="${repoindex or None}">
++    <xi:include href="bh_dirlist_thead.html" />
++    <tbody>
++      <py:for each="idx, (reponame, repoinfo, repos, change, err, raw_href) in enumerate(repo.repositories)"
++              py:with="chgset_context = change and context.child('changeset', change.rev, parent=repos.resource);
++                       chgset_view = change and change.is_viewable(perm)">
++        <tr class="${'odd' if idx % 2 else 'even'}">
++          <td class="name">
++            <em py:strip="not err">
++              <b py:strip="repoinfo.alias != ''">
++                <a class="dir" title="View Root Directory"
++                   href="${href.browser(repos.reponame if repos else reponame,
++                                        order=order if order != 'name' else None, desc=desc)}">$reponame</a>
++              </b>
++            </em>
++          </td>
++          <td class="size">
++            <a py:if="raw_href" class="trac-ziplink" href="$raw_href" title="Download as Zip archive">&#8203;<i class="icon-download-alt"></i></a>
++          </td>
++          <td class="rev">
++            <py:if test="change and not err">
++              <a title="View Revision Log" href="${href.log(repos.reponame)}">${repos.display_rev(change.rev)}</a>
++              <a title="View Changeset" class="chgset" href="${href.changeset(change.rev, repos.reponame)}">&nbsp;</a>
++            </py:if>
++          </td>
++          <td class="age" style="${chgset_view and change and repo.timerange and 'border-color: rgb(%s,%s,%s)' %
++                                   repo.colorize_age(repo.timerange.relative(change.date)) or None}">
++            ${pretty_dateinfo(change.date, dateonly=True) if chgset_view else '&ndash;'}
++          </td>
++          <td class="author">${authorinfo_short(change.author) if chgset_view else '&ndash;'}</td>
++          <td class="change" py:choose="">
++            <py:when test="err"><em py:content="err"></em></py:when>
++            <py:when test="chgset_view" py:choose="">
++              <py:when test="wiki_format_messages">${wiki_to_oneliner(chgset_context, change.message, shorten=True)}</py:when>
++              <py:otherwise>${shorten_line(change.message)}</py:otherwise>
++            </py:when>
++            <py:otherwise>&ndash;</py:otherwise>
++          </td>
++        </tr>
++        <tr class="${'odd' if idx % 2 else 'even'}" py:if="repoinfo.description">
++          <td class="description" colspan="6">${wiki_to_html(context.child('source', '/', parent=repos.resource), repoinfo.description)}</td>
++        </tr>
++      </py:for>
++    </tbody>
++  </table>
++</html>
+diff -r d3aa761ceacc bloodhound_theme/bhtheme/templates/bh_sortable_th.html
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/bloodhound_theme/bhtheme/templates/bh_sortable_th.html	Wed Jan 16 01:51:30 2013 -0500
+@@ -0,0 +1,23 @@
++<!--! Snippet for a <th> corresponding to a sortable column.
++
++    Expects the following variables to be set specifically:
++
++    class_    the CSS class for the column
++    title     the title attribute for the column
++
++    e.g.  <py:with vars="class_ = 'name'; title = 'Name'">
++            <xi:include href="sortable_th.html" />
++          </py:with>
++
++    Expects the following variables from the context: order, desc, href, reponame, path, stickyrev
++-->
++<html xmlns="http://www.w3.org/1999/xhtml"
++    xmlns:py="http://genshi.edgewall.org/"
++    xmlns:xi="http://www.w3.org/2001/XInclude" py:strip="">
++  <th class="$class_${(' desc' if desc else ' asc') if order == class_ else None}">
++    <a title="${_('Sort by %(col)s %(direction)s', col=class_, 
++                  direction=_('(descending)') if order == class_ and not desc else _('(ascending)'))}"
++      href="${href.browser(reponame, path, rev=stickyrev, order=class_ if class_ != 'name' else None,
++                           desc=1 if class_ == order and not desc else None)}">$title <i py:if="order == class_" class="${'icon-chevron-down' if desc else 'icon-chevron-up'}"></i></a>
++  </th>
++</html>
+diff -r d3aa761ceacc bloodhound_theme/bhtheme/theme.py
+--- a/bloodhound_theme/bhtheme/theme.py	Wed Jan 16 01:49:14 2013 -0500
++++ b/bloodhound_theme/bhtheme/theme.py	Wed Jan 16 01:51:30 2013 -0500
+@@ -59,7 +59,7 @@
+     disable_all_trac_css = True
+     BLOODHOUND_KEEP_CSS = set(
+         (
+-            'diff.css',
++            'diff.css', 'code.css'
+         )
+     )
+     BLOODHOUND_TEMPLATE_MAP = {
+@@ -106,6 +106,8 @@
+ 
+         # Version control
+         'revisionlog.html' : ('bh_revisionlog.html', '_modify_generic_vcs'),
++        'browser.html' : ('bh_browser.html', '_modify_browser'),
++        'dir_entries.html' : ('bh_dir_entries.html', None),
+ 
+         # Multi Product
+         'product_view.html' : ('bh_product_view.html', None),
+@@ -326,6 +328,12 @@
+                 path_depth_limit=2
+             ))
+ 
++    def _modify_browser(self, req, template, data, content_type, is_active):
++        """Locate path to file in breadcrumbs area rather than title.
++        """
++        self._modify_generic_vcs(req, template, data, content_type, is_active)
++        add_stylesheet(req, 'theme/css/browser.css')
++
+     # INavigationContributor methods
+ 
+     def get_active_navigation_item(self, req):

File t162/t162_r1433074_bootstrap_revgraph.diff

+# HG changeset patch
+# Parent 9bd4caf1c12acb75fa7d6e2e7b661cd04f3e6d2c
+BH Trac #162 : Rules for revision log to match position of revision graph
+
+diff -r 9bd4caf1c12a bloodhound_theme/bhtheme/htdocs/css/browser.css
+--- a/bloodhound_theme/bhtheme/htdocs/css/browser.css	Wed Jan 16 01:51:31 2013 -0500
++++ b/bloodhound_theme/bhtheme/htdocs/css/browser.css	Wed Jan 16 01:52:45 2013 -0500
+@@ -1,13 +1,3 @@
+-#prefs { margin-top: -0.6em }
+-* html #prefs { width: 34em } /* Set width only for IE */
+-#prefs fieldset { margin: 0; }
+-#prefs fieldset label { display: block }
+-#prefs .buttons { margin-top: -2.3em }
+-#prefs .choice { 
+- float: left; 
+- margin: 0 .6em 0 .3em;
+- border-right: 1px dotted #d7d7d7;
+-}
+ 
+ #file-legend { margin-top: 3em; }
+ 
+@@ -207,3 +197,13 @@
+ #preview .image-file { overflow: hidden }
+ #preview .image-file img { max-width: 100% }
+ 
++/* Corrections to make list look nice on top of Bootstrap */
++
++.chglist td {
++  line-height: 15px;
++}
++
++.chglist .label {
++  font-size: 80%;
++}
++
+diff -r 9bd4caf1c12a bloodhound_theme/bhtheme/theme.py
+--- a/bloodhound_theme/bhtheme/theme.py	Wed Jan 16 01:51:31 2013 -0500
++++ b/bloodhound_theme/bhtheme/theme.py	Wed Jan 16 01:52:45 2013 -0500
+@@ -105,7 +105,7 @@
+         'ticket_preview.html' : ('bh_ticket_preview.html', None),
+ 
+         # Version control
+-        'revisionlog.html' : ('bh_revisionlog.html', '_modify_generic_vcs'),
++        'revisionlog.html' : ('bh_revisionlog.html', '_modify_browser'),
+         'browser.html' : ('bh_browser.html', '_modify_browser'),
+         'dir_entries.html' : ('bh_dir_entries.html', None),
+ 
+@@ -320,18 +320,14 @@
+         """
+         add_script(req, 'dashboard/js/bootstrap-scrollspy.js')
+ 
+-    def _modify_generic_vcs(self, req, template, data, content_type, is_active):
++    def _modify_browser(self, req, template, data, content_type, is_active):
+         """Locate path to file in breadcrumbs area rather than title.
++        Add browser-specific CSS.
+         """
+         data.update(dict(
+                 resourcepath_template='bh_path_links.html',
+                 path_depth_limit=2
+             ))
+-
+-    def _modify_browser(self, req, template, data, content_type, is_active):
+-        """Locate path to file in breadcrumbs area rather than title.
+-        """
+-        self._modify_generic_vcs(req, template, data, content_type, is_active)
+         add_stylesheet(req, 'theme/css/browser.css')
+ 
+     # INavigationContributor methods

File t162/t162_r1433074_bootstrap_revlog.diff

+# HG changeset patch
+# Parent 53ff149b1a1186fa1849b0a001038e925035460d
+BH Theme #162 : Convert revision log web UI to Bootstrap styling
+
+diff -r 53ff149b1a11 bloodhound_theme/bhtheme/htdocs/bloodhound.css
+--- a/bloodhound_theme/bhtheme/htdocs/bloodhound.css	Mon Jan 14 19:36:32 2013 +0000
++++ b/bloodhound_theme/bhtheme/htdocs/bloodhound.css	Wed Jan 16 01:49:14 2013 -0500
+@@ -480,6 +480,10 @@
+   cursor: help;
+ }
+ 
++.breadcrumb .dropdown-menu li {
++  display: block;
++}
++
+ /* Revert some changes introduced in 2.1.0 */
+ 
+ h6 {
+@@ -703,6 +707,11 @@
+   padding: 0.1em 0.25em;
+   background-color: #F7F7F7;
+ }
++
++.more:before {
++  content: "... "
++}
++
+ /* @end */
+ 
+ /* @group Wiki Toolbar */
+@@ -741,3 +750,11 @@
+ 
+ /* @end */
+ 
++/* @group Version control */
++
++.log .age {
++  white-space: nowrap;
++}
++
++/* @end */
++
+diff -r 53ff149b1a11 bloodhound_theme/bhtheme/templates/bh_path_links.html
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/bloodhound_theme/bhtheme/templates/bh_path_links.html	Wed Jan 16 01:49:14 2013 -0500
+@@ -0,0 +1,54 @@
++<div xmlns="http://www.w3.org/1999/xhtml"
++     xmlns:py="http://genshi.edgewall.org/" py:strip="">
++  <!--!  Display a sequence of path components.
++  -
++  -      Each component is a link to the corresponding location in the browser.
++  -
++  -      We expect the following variables to be available in the calling
++  -      context:
++  -       * path_links, a list of dicts each having the following keys:
++  -         * name, the path component name
++  -         * href, a link pointing to this path
++  -       * stickyrev, the optional revision information to show
++  -       * reponame, the optional repository name
++  -       * path_depth_limit, number of path components to show (all if empty) 
++  -->
++<py:def function="part_ui(part, last)"><li><a
++    class="pathentry" href="${part.href}"
++    title="${_('View %(name)s', name=part.name)}">${part.name}</a><span 
++    py:if="not last" class="pathentry sep divider">/</span></li></py:def>
++
++<py:if test="path_links">
++<!--! <realm>: -->
++<li><a class="pathentry first" href="${path_links[0].href}"
++   title="${_('Go to repository index') if reponame else _('Go to repository root')}"><i class="icon-home"></i>${path_links[0].name}</a></li>
++<!--! the/path/.../...
++  -
++  -   Note: in the <py:for>...</py:for> below, we take great care of
++  -         not introducing any space character, so that one can easily
++  -         copy the path to the clipboard.
++  -->
++<py:with vars="_depth = path_depth_limit or 9999;
++  path_lead = path_links[1:2];
++  path_hidden = path_links[2:][:-_depth];
++  path_visible = path_links[2:][-_depth:]"><py:if
++    test="path_lead">${part_ui(path_lead[0], False)}</py:if><py:if
++    test="path_hidden"><li class="dropdown"><a 
++    href="#" class="btn btn-mini dropdown-toggle more" 
++    data-toggle="dropdown"><b class="caret"></b></a><ul
++    class="dropdown-menu"><li 
++    py:for="idx, part in enumerate(path_hidden)"
++    py:with="last = idx == len(path_hidden) - 1"><a 
++      class="pathentry" href="${part.href}"
++      title="${_('View %(name)s', name=part.name)}">${part.name}<span 
++      py:if="not last" class="pathentry sep">/</span></a></li></ul></li><span 
++    class="pathentry sep divider">/</span></py:if><py:for 
++    each="idx, part in enumerate(path_visible)"><py:with 
++    vars="last = idx == len(path_visible) - 1;
++    ">${part_ui(part, last)}</py:with></py:for></py:with></py:if>
++<py:if test="stickyrev" py:with="drev = display_rev(stickyrev)"><span class="pathentry sep">@</span>
++  <a class="pathentry" href="${href.changeset(stickyrev, reponame)}"
++     title="${_('View changeset %(rev)s', rev=drev)}">$drev</a>
++</py:if>
++
++</div>
+diff -r 53ff149b1a11 bloodhound_theme/bhtheme/templates/bh_revisionlog.html
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/bloodhound_theme/bhtheme/templates/bh_revisionlog.html	Wed Jan 16 01:49:14 2013 -0500
+@@ -0,0 +1,253 @@
++<!--!
++  Licensed to the Apache Software Foundation (ASF) under one
++  or more contributor license agreements.  See the NOTICE file
++  distributed with this work for additional information
++  regarding copyright ownership.  The ASF licenses this file
++  to you under the Apache License, Version 2.0 (the
++  "License"); you may not use this file except in compliance
++  with the License.  You may obtain a copy of the License at
++
++  http://www.apache.org/licenses/LICENSE-2.0
++
++  Unless required by applicable law or agreed to in writing,
++  software distributed under the License is distributed on an
++  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++  KIND, either express or implied.  See the License for the
++  specific language governing permissions and limitations
++  under the License.
++-->
++
++<!DOCTYPE html
++    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
++    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
++<html xmlns="http://www.w3.org/1999/xhtml"
++      xmlns:py="http://genshi.edgewall.org/"
++      xmlns:i18n="http://genshi.edgewall.org/i18n"
++      xmlns:xi="http://www.w3.org/2001/XInclude">
++  <xi:include href="layout.html" />
++  <head>
++    <title>${'/'.join(part.name for part in path_links[1:]) or '/'} (log)</title>
++    <script py:if="graph" type="text/javascript">//<![CDATA[
++      jQuery(document).ready(function($) {
++        $('th.trac-graph, td.trac-graph').show();
++        var canvas = $('<canvas>').css({width: '${graph.columns * 2}em',
++                                        height: '${len(items) * 2}em'})
++                                  .appendTo('td.trac-graph')[0];
++        canvas.width = $(canvas).width();
++        canvas.height = $(canvas).height();
++        if (typeof(G_vmlCanvasManager) != 'undefined')
++          canvas = G_vmlCanvasManager.initElement(canvas);
++        $.paintLogGraph(graph, canvas);
++      });
++    //]]></script>
++  </head>
++
++  <body>
++    <div id="content" class="log row">
++      <div class="span4">
++        <h1>Revision log</h1>
++
++        <div class="diff">
++          <div class="legend" id="file-legend">
++            <h3>Legend:</h3>
++            <dl>
++              <dt class="add"></dt><dd>Added</dd>
++              <py:if test="mode == 'path_history'">
++                <dt class="rem" ></dt><dd>Removed</dd>
++              </py:if>
++              <dt class="mod"></dt><dd>Modified</dd>
++              <dt class="cp"></dt><dd>Copied or renamed</dd>
++            </dl>
++          </div>
++        </div>
++      </div>
++      <div class="span8">
++        <div class="well">
++          <form id="prefs" action="" method="get">
++            <div class="form-horizontal">
++              <input type="hidden" name="action" value="$mode" />
++              <input type="hidden" name="revs" value="${str(revranges)}" py:if="revranges" />
++              <div class="choice">
++                <fieldset>
++                  <legend>Revision Log Mode:</legend>
++                  <label class="radio inline">
++                    <input type="radio" id="stop_on_copy" name="mode"
++                           value="stop_on_copy"
++                           checked="${mode in (None, 'stop_on_copy') or None}" />
++                    Stop on copy
++                  </label>
++                  <label class="radio inline">
++                    <input type="radio" id="follow_copy" name="mode"
++                           value="follow_copy"
++                           checked="${mode == 'follow_copy' or None}" />
++                    Follow copies
++                  </label>
++                  <label class="radio inline">
++                    <input type="radio" id="path_history" name="mode"
++                           value="path_history"
++                           checked="${mode == 'path_history' or None}" />
++                    Show only adds and deletes
++                  </label>
++                </fieldset>
++              </div>
++              <div class="control-group">
++                <i18n:msg>
++                  <label class="control-label">View log starting at</label>
++                  <div class="controls">
++                    <input type="text" id="rev" name="rev" value="$rev" size="5" />
++                  </div>
++                  <label class="control-label">and back to</label>
++                  <div class="controls">
++                    <input type="text" id="stop_rev" name="stop_rev" value="$stop_rev" size="5" />
++                  </div>
++                </i18n:msg>
++                <i18n:msg>
++                  <label class="control-label">Show at most</label>
++                  <div class="controls">
++                    <input type="text" id="limit" name="limit" value="$limit" 
++                        class="input-mini" size="3" maxlength="3" />
++                    revisions per page.
++                  </div>
++                </i18n:msg>
++                <div class="controls">
++                  <label>
++                    <input type="checkbox" id="verbose" name="verbose" checked="${verbose or None}" />
++                    Show full log messages
++                  </label>
++                </div>
++              </div>
++              <div class="buttons controls">
++                <input class="btn" type="submit" value="${_('Update')}" />
++              </div>
++            </div>
++          </form>
++        </div>
++      </div>
++
++      <form py:for="items in item_ranges" class="printableform" action="${href.changeset()}" method="get">
++        <div class="buttons">
++          <input type="hidden" name="reponame" value="$reponame" />
++          <input class="btn" type="submit" value="${_('View changes')}"
++            title="Diff from Old Revision to New Revision (as selected in the Diff column)" />
++        </div>
++        <table class="table table-condensed table-striped listing chglist${' trac-graph' if graph else None}">
++          <thead>
++            <tr>
++              <th py:if="graph" class="trac-graph">Graph</th>
++              <th class="diff" title="Old / New" 
++                  style="width: 30px">Diff</th>
++              <th class="change"></th>
++              <th class="rev">Rev</th>
++              <th class="age">Age</th>
++              <th class="author">Author</th>
++              <th class="summary"><py:if test="not verbose">Log Message</py:if></th>
++            </tr>
++          </thead>
++          <tbody>
++            <tr py:if="not items" class="even">
++              <td py:if="graph" class="trac-graph"/>
++              <td />
++              <td />
++              <td class="copyfrom_path" colspan="6">
++                No revisions found
++              </td>
++            </tr>
++
++            <py:for each="idx, item in enumerate(items)">
++              <py:with vars="change = changes[item.rev];
++                             is_separator = item.change is None;
++                             chgset_context = context.child('changeset', change.rev, parent=repos.resource);
++                             odd_even = 'odd' if idx % 2 else 'even'">
++                <!--! highlight copy or rename operations -->
++                <tr py:if="not is_separator and item.get('copyfrom_path')" class="$odd_even">
++                  <td />
++                  <td class="copyfrom_path" colspan="6" style="padding-left: ${item.depth - 1}em">
++                    <i class="icon-info-sign"></i>
++                    <i18n:msg params="path">copied from <a href="${href.browser(reponame, item.path, rev=item.rev)}">$item.copyfrom_path</a>:</i18n:msg>
++                  </td>
++                </tr>
++
++                <tr class="${classes(odd_even,verbose=verbose)}" py:choose="">
++                  <td py:if="graph and idx == 0" class="trac-graph" rowspan="${len(items)}"/>
++                  <td class="diff">
++                    <input type="radio" name="old" value="${item.rev}@${item.path}"
++                           checked="${idx == (len(items) - 1) or None}" 
++                           title="${_('From [%(rev)s]', rev=display_rev(item.rev))}"
++                           class="pull-left" />
++                    <input type="radio" name="new" value="${item.rev}@${item.path}"
++                           checked="${idx == 0 or None}" 
++                           title="${_('To [%(rev)s]', rev=display_rev(item.rev))}" 
++                           class="pull-left" />
++                  </td>
++                  <py:when test="not is_separator">
++                    <td class="change" style="padding-left: ${item.depth}em">
++                      <a href="${href.log(reponame, item.path, rev=item.rev)}"
++                         title="View log starting at this revision">
++                        <span class="$item.change"></span>
++                        <span class="label comment">$item.change</span>
++                      </a>
++                    </td>
++                    <td class="rev">
++                      <a href="${href.browser(reponame, item.path, rev=item.existing_rev)}"
++                         title="${_('Browse at revision %(rev)s', rev=display_rev(item.existing_rev))}">
++                        @${display_rev(item.existing_rev)}</a>
++                      <py:choose test="item.change">
++                        <a py:when="'delete'" class="chgset" href="${href.changeset(item.rev)}"
++                           title="${_('View removal changeset [%(rev)s]', rev=display_rev(item.rev))}">&nbsp;</a>
++                        <a py:otherwise="" class="chgset" href="${href.changeset(item.rev, reponame, item.path)}"
++                           title="${_('View changeset [%(rev)s] restricted to %(path)s',
++                                      rev=display_rev(item.rev), path=item.path or '/')}">&nbsp;</a>
++                      </py:choose>
++                    </td>
++                    <td class="age" py:content="pretty_dateinfo(change.date, dateonly=True)" />
++                    <td class="author" py:content="authorinfo_short(change.author)" />
++                    <td class="summary" py:choose=""
++                        style="${graph and ('border-left: 2px solid '
++                                            + graph.colors[graph.vertices[idx][1] % len(graph.colors)]) or None}">
++                      <py:when test="verbose"></py:when>
++                      <py:when test="wiki_format_messages">
++                        ${wiki_to_oneliner(chgset_context, change.message, shorten=True)}
++                      </py:when>
++                      <py:otherwise>${shorten_line(change.message)}</py:otherwise>
++                      <py:for each="branch, head in change.get_branches()" >
++                        <span py:if="branch not in ('default', 'master')"
++                            class="label label-inverse branch${' head' if head else ''}"
++                            title="${_('Branch head') if head else _('Branch')}">${branch}</span>
++                      </py:for>
++                      <py:for each="tag in change.get_tags()">
++                        <span class="tag label label" title="Tag">${tag}</span>
++                      </py:for>
++                    </td>
++                  </py:when>
++                  <td colspan="5" py:otherwise="" />
++                </tr>
++
++                <tr py:if="verbose and not is_separator" class="summary verbose $odd_even">
++                  <td class="filler" colspan="2" />
++                  <td class="log" colspan="4" py:choose="" xml:space="preserve">
++                    <py:when test="wiki_format_messages">
++                      ${wiki_to_html(chgset_context, change.message, escape_newlines=True)}
++                    </py:when>
++                    <py:otherwise><pre>${change.message}</pre></py:otherwise>
++                  </td>
++                </tr>
++
++              </py:with>
++            </py:for>
++          </tbody>
++        </table>
++
++        <div py:if="len(items) &gt; 10 and len(item_ranges) == 1" class="buttons">
++          <input class="btn" type="submit" value="${_('View changes')}"
++            title="Diff from Old Revision to New Revision (as selected in the Diff column)" />
++        </div>
++      </form>
++
++      <div id="help" class="pull-right" i18n:msg="">
++        <span class="label label-info">Note:</span> See <a href="${href.wiki('TracRevisionLog')}">TracRevisionLog</a>
++        for help on using the revision log.
++      </div>
++
++    </div>
++  </body>
++</html>
+diff -r 53ff149b1a11 bloodhound_theme/bhtheme/theme.py
+--- a/bloodhound_theme/bhtheme/theme.py	Mon Jan 14 19:36:32 2013 +0000
++++ b/bloodhound_theme/bhtheme/theme.py	Wed Jan 16 01:49:14 2013 -0500
+@@ -104,6 +104,9 @@
+         'ticket.html' : ('bh_ticket.html', '_modify_ticket'),
+         'ticket_preview.html' : ('bh_ticket_preview.html', None),
+ 
++        # Version control
++        'revisionlog.html' : ('bh_revisionlog.html', '_modify_generic_vcs'),
++
+         # Multi Product
+         'product_view.html' : ('bh_product_view.html', None),
+ 
+@@ -315,6 +318,14 @@
+         """
+         add_script(req, 'dashboard/js/bootstrap-scrollspy.js')
+ 
++    def _modify_generic_vcs(self, req, template, data, content_type, is_active):
++        """Locate path to file in breadcrumbs area rather than title.
++        """
++        data.update(dict(
++                resourcepath_template='bh_path_links.html',
++                path_depth_limit=2
++            ))
++
+     # INavigationContributor methods
+ 
+     def get_active_navigation_item(self, req):