Merge remote branch 'broquaint/master'
Tomas Doran [Wed, 14 Apr 2010 20:30:55 +0000 (21:30 +0100)]
* broquaint/master:
  rearraged code
  Fixed bug where C<-T>/C<-B> were being called on strings.
  removed debug
  add the image and also set .gititnore to ignore Thumbs.db
  added dropdown nav to actions so can toggle between blob, raw, blame and history
  Updated an attributions in Changes (thanks again Foxtons web designers!).
  Bumped version, updated changes.
  Implemented necessary functionality so binary files no longer render literally.
  Fixed src and dst in diff data for files that had been added/deleted/etc.
  Fixed tree link in the case of no sha1 in the captures.
  Remove the description if it is the default "Unnamed repo..."
  Fix pervasive repo_dir -> repos_dir typo
  added compare diffs feature and fixed a couple of css issues

24 files changed:
.gitignore
Changes
Makefile.PL
README
lib/Gitalist.pm
lib/Gitalist/Controller/Fragment/Ref.pm
lib/Gitalist/Controller/Ref.pm
lib/Gitalist/Git/Object/Commit.pm
lib/Gitalist/Git/Repository.pm
lib/Gitalist/Utils.pm
root/_diff.tt2
root/fragment/collectionofrepositories.tt2
root/fragment/ref/blob.tt2
root/fragment/ref/commit.tt2
root/fragment/ref/history.tt2
root/fragment/repository/shortlog.tt2
root/ref/blob.tt2
root/ref/tree.tt2
root/static/css/core.css
root/static/i/arrow_down_white.gif [new file with mode: 0755]
root/static/i/bg_top.png
root/static/i/icons/Thumbs.db
root/static/js/common.js [new file with mode: 0755]
root/wrapper.tt2

index b22aaa5..ccafab4 100644 (file)
@@ -1,3 +1,4 @@
+Thumbs.db
 Gitalist-*
 *.swp
 git-daemon-export-ok
diff --git a/Changes b/Changes
index e9c27bd..7e5894a 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,11 +1,18 @@
 This file documents the revision history for Perl extension Gitalist.
 
+0.000006 2010-04-10
+
+    - Major frontend redesign, thanks to ranguard and the web designers at
+      Foxtons for making this happen.
+    - Major URI overhaul, Gitalist has gone from old gitweb style CGI
+      parameters to proper URIs, however the old URIs are still supported
+      and will redirect appropriately (Tomas Doran).
+    - With the URI overhaul also came breaking up actions into fragments
+      for use with Catalyst::View::Component::SubInclude.
     - BIG BREAKING CHANGE - Gitalist::Model::GitRepos has been renamed
       Gitalist::Model::CollectionOfRepos. You need to fix your config and
       if you have actually installed Gitalist - remove the old model file.
-
     - Bump required version of Git::PurePerl for Encoding fixes.
-
     - Fix Makefile.PL to not need release deps when checking out of Git.
 
 0.000005 2010-01-09
index d733e1c..6abc413 100644 (file)
@@ -83,6 +83,8 @@ requires 'CGI';
 requires 'DateTime';
 requires 'DateTime::Format::Mail';
 requires 'File::Copy::Recursive';
+requires 'File::Type';
+requires 'File::Type::WebImages';
 requires 'File::Which';
 requires 'HTML::Entities';
 requires 'IPC::Run';
diff --git a/README b/README
index dcdf72f..2de1bfa 100644 (file)
--- a/README
+++ b/README
@@ -73,7 +73,7 @@ INITIAL CONFIGURATION
 
       cp `perl -Ilib -MGitalist -e'print Gitalist->path_to("gitalist.conf")'` gitalist.conf
 
-    You can then edit this confg, adding a repos_dir path and customising
+    You can then edit this confg, adding a repo_dir path and customising
     other settings as desired.
 
     You can then start the Gitalist demo server by setting
@@ -83,15 +83,15 @@ INITIAL CONFIGURATION
 
     Alternatively, if you only want to set a repository directory and are
     otherwise happy with the default configuration, then you can set the
-    "GITALIST_REPO_DIR" environment variable, or pass the "--repos_dir" flag
+    "GITALIST_REPO_DIR" environment variable, or pass the "--repo_dir" flag
     to any of the scripts.
 
         GITALIST_REPO_DIR=/home/myuser/code/git gitalist_server.pl
-        gitalist_server.pl --repos_dir home/myuser/code/git
+        gitalist_server.pl --repo_dir home/myuser/code/git
 
     The "GITALIST_REPO_DIR" environment variable will override the
     repository directory set in configuration, and will itself be overridden
-    by he "--repos_dir" flag.
+    by he "--repo_dir" flag.
 
 RUNNING
     Once you have followed the instructions above to install and configure
index 1be66c8..b4b74ba 100644 (file)
@@ -14,7 +14,7 @@ use Catalyst qw/
                 SubRequest
 /;
 
-our $VERSION = '0.000006_01';
+our $VERSION = '0.000006';
 $VERSION = eval $VERSION;
 
 __PACKAGE__->config(
@@ -139,7 +139,7 @@ by running:
 
   cp `perl -Ilib -MGitalist -e'print Gitalist->path_to("gitalist.conf")'` gitalist.conf
 
-You can then edit this confg, adding a repos_dir path and customising other settings as desired.
+You can then edit this confg, adding a repo_dir path and customising other settings as desired.
 
 You can then start the Gitalist demo server by setting C<< GITALIST_CONFIG >>. For example:
 
@@ -147,13 +147,13 @@ You can then start the Gitalist demo server by setting C<< GITALIST_CONFIG >>. F
 
 Alternatively, if you only want to set a repository directory and are otherwise happy with
 the default configuration, then you can set the C<< GITALIST_REPO_DIR >> environment
-variable, or pass the C<< --repos_dir >> flag to any of the scripts.
+variable, or pass the C<< --repo_dir >> flag to any of the scripts.
 
     GITALIST_REPO_DIR=/home/myuser/code/git gitalist_server.pl
-    gitalist_server.pl --repos_dir home/myuser/code/git
+    gitalist_server.pl --repo_dir home/myuser/code/git
 
 The C<< GITALIST_REPO_DIR >> environment variable will override the repository directory set
-in configuration, and will itself be overridden by he C<< --repos_dir >> flag.
+in configuration, and will itself be overridden by he C<< --repo_dir >> flag.
 
 =head1 RUNNING
 
index b4a2181..65e4a61 100644 (file)
@@ -8,6 +8,8 @@ with qw/
     Gitalist::URIStructure::Fragment::WithLog
 /;
 
+use File::Type::WebImages ();
+
 sub base : Chained('/fragment/repository/find') PathPart('') CaptureArgs(0) {}
 
 sub _diff {
@@ -52,7 +54,6 @@ after tree => sub {
     $c->stash(
         tree      => $tree,
         tree_list => [$repository->list_tree($tree->sha1)],
-        path      => $c->stash->{filename}, # FIXME?
     );
 };
 
@@ -83,7 +84,9 @@ after blob => sub {
     my ( $self, $c ) = @_;
     $c->stash(
         # XXX Hack hack hack, see View::SyntaxHighlight
-        language => ($c->stash->{filename} =~ /\.p[lm]$/i ? 'Perl' : ''),
+        language  => ($c->stash->{filename} =~ /\.p[lm]$/i ? 'Perl' : ''),
+        is_image  => File::Type::WebImages::mime_type($c->stash->{blob}),
+        is_binary => Gitalist::Utils::is_binary($c->stash->{blob}),
     );
 
     $c->forward('View::SyntaxHighlight')
index e43bd98..207d4ca 100644 (file)
@@ -6,6 +6,9 @@ use namespace::autoclean;
 BEGIN { extends 'Gitalist::Controller' }
 with 'Gitalist::URIStructure::Ref';
 
+use File::Type;
+use File::Type::WebImages ();
+
 sub base : Chained('/repository/find') PathPart('') CaptureArgs(0) {}
 
 after commit => sub {
@@ -20,7 +23,16 @@ sub raw : Chained('find') Does('FilenameArgs') Args() {
     my ($self, $c) = @_;
     $c->forward('find_blob');
 
-    $c->response->content_type('text/plain; charset=utf-8');
+    if(!Gitalist::Utils::is_binary($c->stash->{blob})) {
+        $c->response->content_type('text/plain; charset=utf-8');
+    } else {
+        my $ft = File::Type->new();
+        $c->response->content_type(
+            File::Type::WebImages::mime_type($c->stash->{blob})
+         || File::Type->new->mime_type($c->stash->{blob})
+        );
+    }
+
     $c->response->body(delete $c->stash->{blob});
 }
 
index 7ac568b..0445999 100644 (file)
@@ -128,7 +128,7 @@ class Gitalist::Git::Object::Commit
                     next;
                 }
 
-                if (/^index (\w+)\.\.(\w+) (\d+)$/) {
+                if (/^index (\w+)\.\.(\w+)(?: (\d+))?$/) {
                     @{$ret[-1]}{qw(index src dst mode)} = ($_, $1, $2, $3);
                     next
                 }
index 7e873cd..0d0c884 100644 (file)
@@ -135,7 +135,7 @@ class Gitalist::Git::Repository with Gitalist::Git::HasUtils {
             if !$sha1 || $sha1 !~ $SHA1RE;
 
         my @search_opts;
-        if ($search) {
+        if ($search and exists $search->{text}) {
             $search->{type} = 'grep'
                 if $search->{type} eq 'commit';
             @search_opts = (
index 0df4973..c40d825 100644 (file)
@@ -45,6 +45,12 @@ sub age_string {
   return $age_str;
 }
 
+sub is_binary {
+  my($str) = @_;
+  open my $fh, '<', \$str or return;
+  return -B $fh;
+}
+
 1;
 
 __END__
@@ -57,7 +63,11 @@ Gitalist::Utils - trivial utils for Gitalist
 
 =head2 age_string
 
-Turns an integer number of seconds into a string..
+Turns an integer number of seconds into a string.
+
+=head2 is_binary
+
+Check whether a string is binary according to C<-B>.
 
 =head1 AUTHORS
 
index 4b217d2..11b4199 100755 (executable)
@@ -7,13 +7,19 @@
 [% FOREACH item IN diff %]
 
 <h4 id="diff[% loop.count %]" class='diff-head'>diff --git [%# FIXME %]
- <a href='[% c.uri_for_action("/ref/blob", [Repository.name, Commit.sha1], item.file) %]' title="Blob">[% item.a %]</a>
- <a href='[% c.uri_for_action("/ref/blob", [Repository.name, Commit.sha1], item.file) %]' title="Blob">[% item.b %]</a>
+ [% IF !item.src.match('^0+$') %]
+   <a href='[% c.uri_for_action("/ref/blob", [Repository.name, item.src]) %]' title="Blob">[% item.a %]</a>
+ [% ELSE %]
+   [% item.a %]
+ [% END %]
+ [% IF !item.dst.match('^0+$') %]
+ <a href='[% c.uri_for_action("/ref/blob", [Repository.name, item.dst]) %]' title="Blob">[% item.b %]</a>
+ [% ELSE %]
+   [% item.b %]
+ [% END %]
 </h4>
 
 
-
-
 <div class='diff-patch'>
        <pre>[% blobs.${loop.index} %]</pre>
 </div>
index afcc4fd..2b153d3 100755 (executable)
@@ -4,7 +4,7 @@
 <tr [% "class='invert'" IF loop.count % 2 %]>
     <td>[% loop.count %]</td>
        <td><a href="[% repos_link %]/shortlog"><strong>[% p.name %]</strong></a></td>
-    <td><span title="[% p.description %]">[% abridged_description(p.description) %]</span></td>
+    <td class="description"><div title="[% p.description %]">[% abridged_description(p.description) IF p.description != "Unnamed repository; edit this file to name it for gitweb." %]</div></td>
     <td class="time-since">[% time_since(p.last_change) %]</td>
     <td>[% p.owner %]</td>
     <td class="action-list">
index 592f8c5..aaaa65d 100644 (file)
@@ -1 +1,7 @@
-[% blob %]
+[%- IF is_image -%]
+<div class='blob'><img src="[% c.uri_for_action('/ref/raw', c.req.captures, filename) %]" title="[% filename %]"></div>
+[%- ELSIF is_binary -%]
+<div class='blob'>This is a binary file which won't render natively on the web, but you can get it here all the same: <a href="[% c.uri_for_action('/ref/raw', c.req.captures, filename) %]" title="[% filename %]">[% filename %]</a></div>
+[%- ELSE -%]
+<pre class='blob'>[% blob | html %]</pre>
+[%- END -%]
index 551d103..8560e1f 100755 (executable)
@@ -38,7 +38,7 @@
        <tr>
                <td class='sha1'>[% INCLUDE 'inc/chroma_hash.tt2' sha1 = Commit.sha1.substr(0, 7), hide_sha1_output = 1 %] <div class="sha1_label">Commit</div></td>
                <td>[% Commit.sha1 %]</td>
-               <td class='action-list'></td>
+               <td class='action-list'><a href="[% c.uri_for_action('/ref/diff_fancy', [Repository.name, Commit.sha1]) %]" title="Difference" class="button diff">diff</a></td>
        </tr>
        <tr>
                <td class='sha1'>[% INCLUDE 'inc/chroma_hash.tt2' sha1 = Commit.tree_sha1.substr(0, 7), hide_sha1_output = 1 %] <div class="sha1_label">Tree</div></td>
index 92b22b1..0e5ee10 100755 (executable)
@@ -1,6 +1,8 @@
 [% BLOCK history_table_headfoot %]
 [% SET cell = type == 'head' ? 'th' : 'td' %]
+[%# FIXME: should  c.req.arguments.0 be path instead? %]
 <tr>
+<[% cell %] colspan="2"><a href="#" onclick="compareDiffs('[% Repository.name %]','[% c.req.arguments.0 %]');return false;">Compare</a></[% cell %]>
  <[% cell %]>sha1</[% cell %]>
  <[% cell %]>time</[% cell %]>
  <[% cell %]>message</[% cell %]>
  <[% cell %]>actions</[% cell %]>
 </tr>
 [% END %]
+<form name="theform">
  <table class='listing'>
    <thead>[% PROCESS history_table_headfoot type = 'head' %]</thead
 
    <tbody>
    [% FOREACH line IN log_lines %]
  <tr [% "class='invert'" IF loop.count % 2 %]>
+       <td><input type="radio" name="sha1_a" value="[% line.sha1 %]" [% "checked" IF loop.count == 2 %] /></td>
+       <td><input type="radio" name="sha1_b" value="[% line.sha1 %]" [% "checked" IF loop.count == 1 %]/></td>
      <td class='sha1' title='[% line.sha1 %]'>[% INCLUDE 'inc/chroma_hash.tt2' sha1 = line.sha1.substr(0, 7) %]</td>
      <td class='time-since' title='[% line.authored_time %]'>[% time_since(line.authored_time) %]</td>
     
      </td>
     </tr>
    [% END %]
+  <tr>
+       <td colspan="2"><a href="#" onclick="compareDiffs('[% Repository.name %]');return false;">Compare</a></td>
+       <td colspan="5"></td>
+</tr>
    </tbody>
   </table>
+</form>
 [%
   INCLUDE 'inc/log_pager.tt2';
 %]
index 68167b8..bd9f16d 100755 (executable)
@@ -1,6 +1,7 @@
 [% BLOCK shortlog_table_headfoot %]
 [% SET cell = type == 'head' ? 'th' : 'td' %]
 <tr>
+       <[% cell %] colspan="2"><a href="#" onclick="compareDiffs('[% Repository.name %]');return false;">Compare</a></[% cell %]>
        <[% cell %]>ID (sha1)</[% cell %]>
        <[% cell %]>Last change</[% cell %]>
        <[% cell %]>Message</[% cell %]>
 </tr>
 [% END %]
 
+<form name="theform">
 <table class='listing'>
 <thead>[% PROCESS shortlog_table_headfoot type = 'head' %]</thead>
 <tbody>
  [% FOREACH line IN log_lines %]
  <tr [% "class='invert'" IF loop.count % 2 %]>
+       <td><input type="radio" name="sha1_a" value="[% line.sha1 %]" [% "checked" IF loop.count == 2 %] /></td>
+       <td><input type="radio" name="sha1_b" value="[% line.sha1 %]" [% "checked" IF loop.count == 1 %]/></td>
        <td class='sha1' title='[% line.sha1 %]'>[% INCLUDE 'inc/chroma_hash.tt2' sha1 = line.sha1.substr(0, 7) %]</td>
        <td class='time-since' title='[% line.authored_time %]'>[% time_since(line.authored_time) %]</td>
        <td>
        </td>
 </tr>
 [% END %]
+<tr>
+       <td colspan="2"><a href="#" onclick="compareDiffs('[% Repository.name %]');return false;">Compare</a></td>
+       <td colspan="5"></td>
+</tr>
 </tbody>
 </table>
+</form>
 
 [% INCLUDE 'inc/log_pager.tt2' %]
index ad81803..fb4497d 100755 (executable)
@@ -7,8 +7,8 @@
     [% short_cmt(head.comment) %]
    </div>
   [% END %]
-       <pre class='blob'>[% subinclude('/fragment/ref/blob', c.req.captures, c.req.args.to_path) | html %]</pre>
+
+[% subinclude('/fragment/ref/blob', c.req.captures, c.req.args.to_path) %]
 
 
  
\ No newline at end of file
index b477e0c..6d76c32 100755 (executable)
@@ -1,8 +1 @@
-  [%
-    IF path;
-      INCLUDE 'nav/path.tt2' filename = path;
-    END;
-
-    subinclude('/fragment/ref/tree', c.req.captures, c.req.args.to_path);
-  %]
-
+[% subinclude('/fragment/ref/tree', c.req.captures, c.req.args.to_path) %]
index 698445d..befb5d2 100755 (executable)
@@ -79,7 +79,7 @@ a img{
 
 /* nav tabs */
 #nav_logs{
-       width:80%;
+       width:100%;
        clear:both;
        float:right;
        margin:-5px 10px 0 0;
@@ -180,7 +180,45 @@ a:hover{
        color:#DC143C;
 }
 
+/* sub actions dropdown changer in h1 */
+#actions_nav_link{
+       border:1px solid #666;
+       padding:1px 4px;
+       text-decoration:none;
+       outline:none;
+}
+#actions_nav_link span{
+       margin-right:5px;
+       padding-right:18px;
 
+       background:transparent url([% c.uri_for('/static/i/arrow_down_white.gif') %]) no-repeat right center;
+}
+#actions_nav_list{
+       display:none;
+       position:absolute;
+       padding:0 0 3px 0;
+       margin:0;
+       background-color:#333;
+       color:#fff;
+       font-size:1em;
+       border:1px solid #666;
+       border-top:none;
+}
+#actions_nav_list li{
+       list-style:none;
+       margin:0;
+       padding:2px 10px 2px 5px;
+}
+#actions_nav_list a{
+       color:#fff;
+       font-size:1.8em;
+}
+#actions_nav_list a:hover{
+       color:#EAF2F5;
+}
+.actions_nav_list_over{
+       display:block !important;
+}
 .button{
        display:block;
        float:left;
@@ -303,6 +341,9 @@ th{
 th a{
        color:#fff;
 }
+th a:hover{
+       color:#f0f0f0;
+}
 .summary tr{
        background-color:#FAFAFA;
        border-bottom:1px solid #fff;
@@ -310,6 +351,12 @@ th a{
 .summary td{
        vertical-align:middle !important;
 }
+tr{
+       background-color:#fff;
+}
+thead tr{
+       background-color:transparent !important;
+}
 tr.invert{
        background-color:#f0f0f0;
 }
@@ -341,6 +388,24 @@ table.listing{
        vertical-align:top;
        padding:9px 5px 9px 10px;
 }
+
+/*
+
+puts the repo description on one line which gets truncated if the repo name is too long
+BUT the final width needs to be set with javascript based on the parent element (td) width
+
+.description{
+       white-space:nowrap;
+       overflow:hidden;
+}
+.description div{
+       position:absolute;
+       white-space:nowrap;
+       overflow:hidden;
+       width:200px;
+}
+*/
+
 .action-list{
        width:120px;
 }
@@ -369,6 +434,7 @@ table.listing{
        padding:10px;
        border:1px solid #ddd;
        background-color:#f0f0f0;
+       min-height:40px;
 }
 .diff-head{
        background-color:#666;
@@ -439,6 +505,10 @@ pre.blob {
   overflow:auto;
   font-size:12px;
 }
+div.blob {
+    text-align: center;
+    margin: 30px;
+}
 
 /* /blobdiff etc */
 
diff --git a/root/static/i/arrow_down_white.gif b/root/static/i/arrow_down_white.gif
new file mode 100755 (executable)
index 0000000..a8ef359
Binary files /dev/null and b/root/static/i/arrow_down_white.gif differ
index 39b765f..65601c4 100755 (executable)
Binary files a/root/static/i/bg_top.png and b/root/static/i/bg_top.png differ
index bdf3302..c804544 100644 (file)
Binary files a/root/static/i/icons/Thumbs.db and b/root/static/i/icons/Thumbs.db differ
diff --git a/root/static/js/common.js b/root/static/js/common.js
new file mode 100755 (executable)
index 0000000..921c4f4
--- /dev/null
@@ -0,0 +1,24 @@
+function compareDiffs(repo, path){
+       var f = document.theform;
+       if(!repo){
+               var repo = "";
+       }
+       if(!path){
+               var path = "";
+       }
+       var sha1,sha2;
+       for(var i=0,len=f.length;i<len;i++){
+               if(f[i].name == "sha1_a"){
+                       if(f[i].checked){
+                               sha1 = f[i].value;
+                       }
+               }
+               if(f[i].name == "sha1_b"){
+                       if(f[i].checked){
+                               sha2 = f[i].value;
+                       }
+               }
+       }
+       //document.location.href = [% c.uri_for("/" + repo + "/"+ sha1 + "/diff/" + sha2 + "/" + path) %];
+       document.location.href = "/" + repo + "/"+ sha1 + "/diff/" + sha2 + "/" + path;
+}
index e42e8d9..7f482d1 100755 (executable)
    
        <link rel="stylesheet" type="text/css" href="[% c.uri_for('/core.css') %]" />
        <link rel="shortcut icon" href="[% c.uri_for('/static/git-favicon.png') %]" type="image/png" />
+       <script type="text/javascript">
+       // FIXME: this should be in an external js file once c.uri_for works in js files
+       function compareDiffs(repo, path){
+               var f = document.theform;
+               if(!repo){
+                       var repo = "";
+               }
+               if(!path){
+                       var path = "";
+               }
+               var sha1,sha2;
+               for(var i=0,len=f.length;i<len;i++){
+                       if(f[i].name == "sha1_a"){
+                               if(f[i].checked){
+                                       sha1 = f[i].value;
+                               }
+                       }
+                       if(f[i].name == "sha1_b"){
+                               if(f[i].checked){
+                                       sha2 = f[i].value;
+                               }
+                       }
+               }
+               document.location.href = "[% c.uri_for("/") %]" + repo + "/"+ sha1 + "/diff/" + sha2 + "/" + path;
+       }
+       // handles hover sub menus in IE
+       startList = function() {
+               if (document.getElementById && document.getElementById("actions_nav_link")) {
+                       var navList = document.getElementById("actions_nav_list");
+                       var navLink = document.getElementById("actions_nav_link");
+                       // assign event handlers to each element
+                       navLink.onmouseover=function() {
+                               setNavClass(navList);
+                       }
+                       navList.onmouseover=function() {
+                               setNavClass(navList);
+                       }
+                       navList.onmouseout=function() {
+                               navList.className=navList.className.replace(" actions_nav_list_over", "");
+                       }
+                       navLink.onmouseout=function() {
+                               navList.className=navList.className.replace(" actions_nav_list_over", "");
+                       }
+               }
+               
+       }
+       function setNavClass(el){
+               var link_el = document.getElementById("actions_nav_link");
+               var offsetAry = findPos(link_el);
+               // set position of list
+               el.style.left = offsetAry[0]+"px";
+               el.style.top = offsetAry[1]+30 +"px";
+               el.className+=" actions_nav_list_over";
+       }
+       
+       function addLoadListener(fn){ 
+               if (typeof window.addEventListener != 'undefined') { 
+                       window.addEventListener('load', fn, false); 
+               }else if (typeof document.addEventListener != 'undefined'){ 
+                       document.addEventListener('load', fn, false); 
+               }else if (typeof window.attachEvent != 'undefined'){ 
+                       window.attachEvent('onload', fn); 
+               }else{ 
+                       var oldfn = window.onload; 
+                       if (typeof window.onload != 'function'){ 
+                               window.onload = fn; 
+                       }else{ 
+                               window.onload = function(){
+                                       oldfn();
+                                       fn();
+                               }; 
+                       }
+               }
+       }
+       function findPos(obj) {
+               var curleft = curtop = 0;
+               if (obj.offsetParent) {
+               do {
+                       curleft += obj.offsetLeft;
+                       curtop += obj.offsetTop;
+               } 
+               while (obj = obj.offsetParent);
+               return [curleft,curtop];
+               }
+       }
+       addLoadListener(startList);
+       </script>
 </head>
 
 <body>
                        [% IF c.req.captures.size == 1; SET path = 'repository'; ELSE; SET path = 'ref'; END %]
                        
                        <ul>
-                               <li[% ' class="selected"' IF c.action.name.match('tree') %]><a href="[% c.uri_for_action('/ref/tree', c.req.captures) %]" id="tree">Tree</a></li>
+                               <li[% ' class="selected"' IF c.action.name.match('tree') %]><a href="[% c.uri_for_action('/ref/tree', c.req.captures) || c.uri_for_action('/repository/tree', c.req.captures) %]" id="tree">Tree</a></li>
 
                                <li[% ' class="selected"' IF c.action.name.match('longlog') %]><a href="[% c.uri_for_action('/' _ path _ '/longlog', c.req.captures) %]" id="log_full">Long log</a></li>
                                
                                [% END %]
                        [%- END %]
                        
+                       
                        [%- 
-                               c.action.name
-                                       .replace("_"," ")
-                                       .replace("log", " log")
-                                       .replace("fancy","")
-                                       .replace("index","Repositories") 
-                                       FILTER ucfirst 
+                               SET actions_list = {
+                                               "blob"          => 1,
+                                               "raw"           => 1,
+                                               "blame"         => 1,
+                                               "history"       => 1,
+                                       };
+                                       
+                               SET action_name = c.action.name
+                                                                       .replace("_"," ")
+                                                                       .replace("log", " log")
+                                                                       .replace("fancy","")
+                                                                       .replace("index","Repositories");
+                               action_name_ucfirst = action_name FILTER ucfirst;
+                               IF actions_list.$action_name;
+                                       '<a href="#" id="actions_nav_link"><span>' _ action_name_ucfirst _ '</span></a>';
+                               ELSE;
+                                       action_name_ucfirst;
+                               END;
+                               
                        -%]
-                       
-       
-                       
+
                        </h1>
        
+                       [%- 
+                               IF actions_list.$action_name;
+                               '<ul id="actions_nav_list">';
+                                       FOREACH action IN actions_list;
+                                               action_output = action.key FILTER ucfirst;
+                                               NEXT IF action_output == action_name_ucfirst;   
+                                               action = action.key;
+                                               '<li><a href="/' _ c.req.path.replace(action_name, action) _ '">' _ action_output _ '</a></li>';
+                                       END;                    
+                               '</ul>';
+                               END;
+                       -%]
+               
        </div>
 </div>
 
-
+       
 <div id="content_holder">
        <div id="content" class="sub_holder">
                <div id="content_inner">
 <div id="footer_holder">
        <div id="footer" class="sub_holder">
                
-               <p>[% IF Repository; Repository.description | html; END; %]</p>
+               <p>[% IF Repository; Repository.description | html IF Repository.description != "Unnamed repository; edit this file to name it for gitweb." ; END; %]</p>
        
                <a title="git homepage" href="http://git-scm.org"><img src="[% c.uri_for('/logo.png') %]" id="git_logo" alt="git" /></a>