changeset 3536:ed48d17836a4 beta

WIP: Changelog view
author Leonardo <leo@unity3d.com>
date Thu, 07 Mar 2013 14:48:23 +0100
parents 2792019caf67
children 7f8d349bbde4
files rhodecode/lib/helpers.py rhodecode/lib/utils2.py rhodecode/public/css/style.css rhodecode/templates/changelog/changelog.html
diffstat 4 files changed, 414 insertions(+), 293 deletions(-) [+]
line wrap: on
line diff
--- a/rhodecode/lib/helpers.py	Thu Mar 07 14:38:40 2013 +0100
+++ b/rhodecode/lib/helpers.py	Thu Mar 07 14:48:23 2013 +0100
@@ -363,7 +363,7 @@
 from rhodecode.lib.utils2 import credentials_filter, age as _age
 from rhodecode.model.db import User, ChangesetStatus
 
-age = lambda  x: _age(x)
+age = lambda  x,y=False: _age(x,y)
 capitalize = lambda x: x.capitalize()
 email = author_email
 short_id = lambda x: x[:12]
--- a/rhodecode/lib/utils2.py	Thu Mar 07 14:38:40 2013 +0100
+++ b/rhodecode/lib/utils2.py	Thu Mar 07 14:48:23 2013 +0100
@@ -349,11 +349,15 @@
     return engine
 
 
-def age(prevdate):
+def age(prevdate, show_short_version=False):
     """
     turns a datetime into an age string.
+    If show_short_version is True, then it will generate a not so accurate but shorter string,
+    example: 2days ago, instead of 2 days and 23 hours ago.
+
 
     :param prevdate: datetime object
+    :param show_short_version: if it should aproximate the date and return a shorter string
     :rtype: unicode
     :returns: unicode words describing age
     """
@@ -423,7 +427,7 @@
         else:
             sub_value = 0
 
-        if sub_value == 0:
+        if sub_value == 0 or show_short_version:
             if future:
                 return _(u'in %s') % fmt_funcs[part](value)
             else:
--- a/rhodecode/public/css/style.css	Thu Mar 07 14:38:40 2013 +0100
+++ b/rhodecode/public/css/style.css	Thu Mar 07 14:48:23 2013 +0100
@@ -1844,14 +1844,18 @@
     padding: 0 3px 2px;
 }
 
-#summary {
-    float: left;
-    width: 80%;
+#content div.box #summary {
+    /*float: left;*/
+    /*width: 80%;*/
+    margin-right: 200px;
 }
 
 #summary-menu-stats{
     float: left;
-    width: 20%;
+    width: 200px;
+    position: absolute;
+    top: 0;
+    right: 0;
 }
 
 #summary-menu-stats ul {
@@ -2521,10 +2525,10 @@
     text-align: right;
 }
 
-#changeset_content .container .left .message {
+#changeset_content .container .message {
     white-space: pre-wrap;
 }
-#changeset_content .container .left .message a:hover {
+#changeset_content .container .message a:hover {
     text-decoration: none;
 }
 .cs_files .cur_cs {
@@ -2620,23 +2624,30 @@
     text-align: left;
 }
 
+.table {
+    position: relative;
+}
+
 #graph {
+    position: relative;
     overflow: hidden;
 }
 
 #graph_nodes {
-    float: left;
-    margin-right: 0px;
-    margin-top: 0px;
+    position: absolute;
+}
+
+#graph_content,
+#graph .info_box,
+#graph .container_header {
+    margin-left: 100px;
 }
 
 #graph_content {
-    width: 80%;
-    float: left;
-}
-
-#graph_content .container_header {
-    border-bottom: 1px solid #DDD;
+    position: relative;
+}
+
+#graph .container_header {
     padding: 10px;
     height: 25px;
 }
@@ -2651,55 +2662,140 @@
     margin: 0px 0px 0px 3px;
 }
 
+#graph_content #changesets {
+    table-layout: fixed;
+    border-collapse: collapse;
+    border-left: none;
+    border-right: none;
+    border-color: #cdcdcd;
+}
+
 #graph_content .container {
-    border-bottom: 1px solid #DDD;
-    height: 56px;
+    min-height: 5em;
+}
+
+#graph_content #changesets td {
     overflow: hidden;
+    text-overflow:ellipsis;
+    white-space: nowrap;
+    height: 31px;
+    border-color: #cdcdcd;
+}
+
+#graph_content .container .author {
+   width: 105px;
+}
+
+#graph_content .container .hash{
+    width: 90px;
+    font-size: 0.85em;
+}
+
+#graph_content #changesets .container .date{
+    width: 76px;
+    color: #666;
+    font-size: 10px;
 }
 
 #graph_content .container .right {
-    float: right;
-    width: 23%;
-    text-align: right;
-}
-
-#graph_content .container .left {
-    float: left;
-    width: 25%;
-    padding-left: 5px;
-}
-
+    width: 120px;
+    padding-right: 0px;
+}
+
+/*
+#graph_content .container .column3 {
+    width: 12%
+}
+*/
 #graph_content .container .mid {
+    position: relative;
+}
+
+
+
+
+#graph_content #changesets td.checkbox{
+    width: 20px;
+}
+
+#graph_content .container .changeset_range {
     float: left;
-    width: 49%;
-}
-
-
-#graph_content .container .left .date {
-    color: #666;
-    padding-left: 22px;
-    font-size: 10px;
-}
-
-#graph_content .container .left .author {
-    height: 22px;
-}
-
-#graph_content .container .left .author .user {
+    margin: 6px 3px;
+}
+
+
+
+#graph_content .container .author img {
+    vertical-align: middle;
+}
+
+#graph_content .container .author .user {
     color: #444444;
-    float: left;
-    margin-left: -4px;
-    margin-top: 4px;
 }
 
 #graph_content .container .mid .message {
     white-space: pre-wrap;
+    padding: 0;
+    overflow: hidden;
+    height: 1.1em;
+}
+
+#graph_content .container .mid .logtags {
+    display: block;
+    position: absolute;
+    top: 0;
+    right: 0;
+    padding: 0 5px;
+    background: #FFFFFF;
+}
+
+#graph_content .right .comments-container,
+#graph_content .right .logtags {
+    display: block;
+    float: left;
+    overflow: hidden;
+    width: 50%;
+    padding: 0;
+    margin: 0;
+}
+
+#graph_content .right .comments-container{
+    width: 40px;
+}
+
+#graph_content .right .logtags {
+    width: 80px;
+    height: 2.5em;
+    position: absolute;
+    left: 40px;
+}
+
+#graph_content .right .logtags:hover {
+    position: absolute;
+    width: auto;
+}
+
+#graph_content .right .logtags .bookbook,
+#graph_content .right .logtags .tagtag {
+    float: left;
+    line-height: 1em;
+    margin-bottom: 1px;
 }
 
 #graph_content .container .mid .message a:hover {
     text-decoration: none;
 }
 
+/*
+ * Stuff we might want to remove from the changelog, or reposition in a tooltip
+ */
+#graph_content .container .changeset_id,
+#graph_content .container .changes,
+#graph_content .container .changed_total,
+#graph_content .container .parent {
+    display: none;
+}
+
 .revision-link {
     color:#3F6F9F;
     font-weight: bold !important;
@@ -2735,17 +2831,18 @@
 .code-header .changeset-status-container .changeset-status-ico, .container .changeset-status-ico {
     float: left;
 }
-.right .comments-container {
-    padding-right: 5px;
-    margin-top:1px;
-    float:right;
-    height:14px;
-}
 
 .right .comments-cnt {
-    float: left;
     color: rgb(136, 136, 136);
-    padding-right: 2px;
+    padding: 5px 0;
+}
+
+.right .comments-cnt a {
+    background-image: url('../images/icons/comments.png');
+    background-repeat: no-repeat;
+    background-position: 100% 50%;
+    padding: 5px 0;
+    padding-right: 20px;
 }
 
 .right .changes {
@@ -2790,7 +2887,6 @@
     padding: 1px 3px 1px 3px;
     background-color: #fca062;
     font-size: 10px;
-    font-weight: bold;
     color: #ffffff;
     text-transform: uppercase;
     white-space: nowrap;
@@ -2804,7 +2900,8 @@
     clear:both;
 }
 .right .logtags {
-    padding: 2px 2px 2px 2px;
+    /*padding: 2px 2px 2px 2px;*/
+    line-height: 2.2em;
 }
 .right .logtags .branchtag, .right .logtags .tagtag, .right .logtags .booktag {
     margin: 0px 2px;
@@ -2814,20 +2911,30 @@
 .logtags .branchtag,
 .spantag {
     padding: 1px 3px 1px 3px;
-    background-color: #bfbfbf;
     font-size: 10px;
-    font-weight: bold;
-    color: #ffffff;
+    color: #336699;
     white-space: nowrap;
-    -webkit-border-radius: 3px;
-    border-radius: 3px;
-}
+    -webkit-border-radius: 4px;
+    border-radius: 4px;
+    border: 1px solid #d9e8f8;
+    line-height: 1.5em;
+    margin: 1.1em 0;
+}
+
+.right .logtags .branchtag,
+.logtags .tagtag,
+.right .merge {
+    float: right;
+    /*height: 1em;*/
+    line-height: 1em;
+    /*padding: 0px 0px !important;*/
+    margin: 1px 1px !important;
+    display: block;
+}
+
 .right .logtags .branchtag a:hover, .logtags .branchtag a {
-    color: #ffffff;
-}
-.right .logtags .branchtag a:hover, .logtags .branchtag a:hover {
     text-decoration: none;
-    color: #ffffff;
+    color: #336699;
 }
 .right .logtags .tagtag, .logtags .tagtag {
     padding: 1px 3px 1px 3px;
@@ -2850,9 +2957,7 @@
     padding: 1px 3px 1px 3px;
     background-color: #46A546;
     font-size: 10px;
-    font-weight: bold;
     color: #ffffff;
-    text-transform: uppercase;
     white-space: nowrap;
     -webkit-border-radius: 3px;
     border-radius: 3px;
@@ -3728,6 +3833,7 @@
 }
 
 #content div.box div.form, #content div.box div.table, #content div.box div.traffic {
+    position: relative;
     clear: both;
     overflow: hidden;
     margin: 0;
@@ -3985,13 +4091,7 @@
     width: 600px;
 }
 
-#changeset_content .container .left {
-    float: left;
-    width: 75%;
-    padding-left: 5px;
-}
-
-#changeset_content .container .left .date, .ac .match {
+#changeset_content .container .date, .ac .match {
     font-weight: 700;
     padding-top: 5px;
     padding-bottom: 5px;
--- a/rhodecode/templates/changelog/changelog.html	Thu Mar 07 14:38:40 2013 +0100
+++ b/rhodecode/templates/changelog/changelog.html	Thu Mar 07 14:48:23 2013 +0100
@@ -16,7 +16,7 @@
 </%def>
 
 <%def name="main()">
- ${self.context_bar('changelog')}
+${self.context_bar('changelog')}
 <div class="box">
     <!-- box / title -->
     <div class="title">
@@ -25,11 +25,7 @@
     <div class="table">
         % if c.pagination:
             <div id="graph">
-                <div id="graph_nodes">
-                    <canvas id="graph_canvas"></canvas>
-                </div>
-                <div id="graph_content">
-                    <div class="info_box" style="clear: both;padding: 10px 6px;text-align: right;">
+                <div class="info_box" style="clear: both;padding: 10px 6px;text-align: right;">
                     <a href="#" class="ui-btn small" id="rev_range_container" style="display:none"></a>
                     <a href="#" class="ui-btn small" id="rev_range_clear" style="display:none">${_('Clear selection')}</a>
 
@@ -39,246 +35,267 @@
                     %if h.is_hg(c.rhodecode_repo):
                     <a id="open_new_pr" href="${h.url('pullrequest_form',repo_name=c.repo_name)}" class="ui-btn small">${_('Open new pull request')}</a>
                     %endif
+                </div>
+                <div class="container_header">
+                    ${h.form(h.url.current(),method='get')}
+                    <div style="float:left">
+                        ${h.submit('set',_('Show'),class_="ui-btn")}
+                        ${h.text('size',size=1,value=c.size)}
+                        ${_('revisions')}
                     </div>
-                    <div class="container_header">
-                        ${h.form(h.url.current(),method='get')}
-                        <div class="info_box" style="float:left">
-                          ${h.submit('set',_('Show'),class_="ui-btn")}
-                          ${h.text('size',size=1,value=c.size)}
-                          ${_('revisions')}
-                        </div>
-                        ${h.end_form()}
+                    ${h.end_form()}
                     <div style="float:right">${h.select('branch_filter',c.branch_name,c.branch_filters)}</div>
-                    </div>
+                </div>
+                <div id="graph_nodes">
+                    <canvas id="graph_canvas"></canvas>
+                </div>
+                <div id="graph_content">
 
+                <table id="changesets">
+                <tbody>
                 %for cnt,cs in enumerate(c.pagination):
-                    <div id="chg_${cnt+1}" class="container ${'tablerow%s' % (cnt%2)}">
-                        <div class="left">
-                            <div>
+                    <tr id="chg_${cnt+1}" class="container ${'tablerow%s' % (cnt%2)}">
+                        <td class="checkbox">
                             ${h.checkbox(cs.raw_id,class_="changeset_range")}
-                            <span class="tooltip" title="${h.tooltip(h.age(cs.date))}"><a href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id)}"><span class="changeset_id">${cs.revision}:<span class="changeset_hash">${h.short_id(cs.raw_id)}</span></span></a></span>
-                            </div>
-                            <div class="author">
-                                <div class="gravatar">
-                                    <img alt="gravatar" src="${h.gravatar_url(h.email_or_none(cs.author),16)}"/>
-                                </div>
-                                <div title="${cs.author}" class="user">${h.shorter(h.person(cs.author),22)}</div>
-                            </div>
-                            <div class="date">${h.fmt_date(cs.date)}</div>
-                        </div>
-                        <div class="mid">
+                        </td>
+                        <td class="author">
+                            <img alt="gravatar" src="${h.gravatar_url(h.email_or_none(cs.author),16)}"/>
+                            <span title="${cs.author}" class="user">${h.shorter(h.person(cs.author),22)}</span>
+                        </td>
+                        <td class="hash">
+                            <span class="tooltip" title="${h.tooltip(h.age(cs.date))}">
+                                <a href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id)}">
+                                    <span class="changeset_id">${cs.revision} :</span>
+                                    <span class="changeset_hash">${h.short_id(cs.raw_id)}</span>
+                                </a>
+                            </span>
+                        </td>
+                        <td class="date">
+                            <div class="date">${h.age(cs.date,True)}</div>
+                        </td>
+                        <td class="mid">
                             <div class="message">${h.urlify_commit(cs.message, c.repo_name,h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</div>
                             <div class="expand"><span class="expandtext">&darr; ${_('Show more')} &darr;</span></div>
-                        </div>
-                        <div class="right">
-                                    <div class="changes">
-                                        <div id="changed_total_${cs.raw_id}" style="float:right;" class="changed_total tooltip" title="${h.tooltip(_('Affected number of files, click to show more details'))}">${len(cs.affected_files)}</div>
-                                        <div class="comments-container">
-                                        %if len(c.comments.get(cs.raw_id,[])) > 0:
-                                            <div class="comments-cnt" title="${('comments')}">
-                                              <a href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id,anchor='comment-%s' % c.comments[cs.raw_id][0].comment_id)}">
-                                               <div class="comments-cnt">${len(c.comments[cs.raw_id])}</div>
-                                               <img src="${h.url('/images/icons/comments.png')}">
-                                              </a>
-                                            </div>
+                            %if cs.branch:
+                                <div class="logtags">
+                                    <div class="branchtag" title="${'%s %s' % (_('branch'),cs.branch)}">
+                                        ${h.link_to(h.shorter(cs.branch),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}
+                                    </div>
+                                </div>
+                            %endif
+                        </td>
+                       
+                        <td class="right">
+                            <div class="changes">
+                                <div id="changed_total_${cs.raw_id}" style="float:right;" class="changed_total tooltip" title="${h.tooltip(_('Affected number of files, click to show more details'))}">
+                                    ${len(cs.affected_files)}
+                                </div>
+                                
+                                <div class="changeset-status-container">
+                                    %if c.statuses.get(cs.raw_id):
+                                        <div title="${_('Changeset status')}" class="changeset-status-lbl">${c.statuses.get(cs.raw_id)[1]}</div>
+                                        <div class="changeset-status-ico">
+                                        %if c.statuses.get(cs.raw_id)[2]:
+                                            <a class="tooltip" title="${_('Click to open associated pull request #%s' % c.statuses.get(cs.raw_id)[2])}" href="${h.url('pullrequest_show',repo_name=c.statuses.get(cs.raw_id)[3],pull_request_id=c.statuses.get(cs.raw_id)[2])}"><img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses.get(cs.raw_id)[0])}" /></a>
+                                        %else:
+                                            <img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses.get(cs.raw_id)[0])}" />
                                         %endif
-                                        </div>
-                                        <div class="changeset-status-container">
-                                            %if c.statuses.get(cs.raw_id):
-                                              <div title="${_('Changeset status')}" class="changeset-status-lbl">${c.statuses.get(cs.raw_id)[1]}</div>
-                                              <div class="changeset-status-ico">
-                                              %if c.statuses.get(cs.raw_id)[2]:
-                                                <a class="tooltip" title="${_('Click to open associated pull request #%s' % c.statuses.get(cs.raw_id)[2])}" href="${h.url('pullrequest_show',repo_name=c.statuses.get(cs.raw_id)[3],pull_request_id=c.statuses.get(cs.raw_id)[2])}"><img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses.get(cs.raw_id)[0])}" /></a>
-                                              %else:
-                                                <img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses.get(cs.raw_id)[0])}" />
-                                              %endif
-                                              </div>
-                                            %endif
-                                        </div>
+                                      </div>
+                                    %endif
+                                </div>
+                            </div>
+                            %if cs.parents:
+                                %for p_cs in reversed(cs.parents):
+                                    <div class="parent">${_('Parent')}
+                                        <span class="changeset_id">${p_cs.revision}:<span class="changeset_hash">${h.link_to(h.short_id(p_cs.raw_id),
+                                    h.url('changeset_home',repo_name=c.repo_name,revision=p_cs.raw_id),title=p_cs.message)}</span></span>
                                     </div>
-                                   %if cs.parents:
-                                    %for p_cs in reversed(cs.parents):
-                                        <div class="parent">${_('Parent')}
-                                            <span class="changeset_id">${p_cs.revision}:<span class="changeset_hash">${h.link_to(h.short_id(p_cs.raw_id),
-                                            h.url('changeset_home',repo_name=c.repo_name,revision=p_cs.raw_id),title=p_cs.message)}</span></span>
-                                        </div>
-                                    %endfor
-                                   %else:
-                                        <div class="parent">${_('No parents')}</div>
-                                   %endif
-
-                                <span class="logtags">
-                                    %if len(cs.parents)>1:
+                                %endfor
+                            %else:
+                                <div class="parent">${_('No parents')}</div>
+                            %endif
+                            <div class="comments-container">
+                                %if len(c.comments.get(cs.raw_id,[])) > 0:
+                                    <div class="comments-cnt" title="${('comments')}">
+                                        <a href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id,anchor='comment-%s' % c.comments[cs.raw_id][0].comment_id)}">
+                                            ${len(c.comments[cs.raw_id])}
+                                        </a>
+                                    </div>
+                                %endif
+                            </div>
+                            <div class="logtags">
+                                %if len(cs.parents)>1:
                                     <span class="merge">${_('merge')}</span>
-                                    %endif
-                                    %if cs.branch:
-                                    <span class="branchtag" title="${'%s %s' % (_('branch'),cs.branch)}">
-                                       ${h.link_to(h.shorter(cs.branch),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}
+                                %endif
+                                
+                                %if h.is_hg(c.rhodecode_repo):
+                                    %for book in cs.bookmarks:
+                                        <span class="bookbook" title="${'%s %s' % (_('bookmark'),book)}">
+                                            ${h.link_to(h.shorter(book),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}
+                                        </span>
+                                  %endfor
+                                %endif
+                                %for tag in cs.tags:
+                                    <span class="tagtag"  title="${'%s %s' % (_('tag'),tag)}">
+                                        ${h.link_to(h.shorter(tag),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}
                                     </span>
-                                    %endif
-                                    %if h.is_hg(c.rhodecode_repo):
-                                      %for book in cs.bookmarks:
-                                      <span class="bookbook" title="${'%s %s' % (_('bookmark'),book)}">
-                                         ${h.link_to(h.shorter(book),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}
-                                      </span>
-                                      %endfor
-                                    %endif
-                                    %for tag in cs.tags:
-                                        <span class="tagtag"  title="${'%s %s' % (_('tag'),tag)}">
-                                        ${h.link_to(h.shorter(tag),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}</span>
-                                    %endfor
-                                </span>
-                        </div>
-                    </div>
+                                %endfor
+                            </div>
+                        </td>
+                    </tr>
 
                 %endfor
+                </tbody>
+                </table>
+
+
                 <div class="pagination-wh pagination-left">
                     ${c.pagination.pager('$link_previous ~2~ $link_next')}
                 </div>
-                </div>
             </div>
+        </div>
 
-            <script type="text/javascript" src="${h.url('/js/graph.js')}"></script>
-            <script type="text/javascript">
-                YAHOO.util.Event.onDOMReady(function(){
+        <script type="text/javascript" src="${h.url('/js/graph.js')}"></script>
+        <script type="text/javascript">
+            YAHOO.util.Event.onDOMReady(function(){
 
-                    //Monitor range checkboxes and build a link to changesets
-                    //ranges
-                    var checkboxes = YUD.getElementsByClassName('changeset_range');
-                    var url_tmpl = "${h.url('changeset_home',repo_name=c.repo_name,revision='__REVRANGE__')}";
-                    var pr_tmpl = "${h.url('pullrequest_home',repo_name=c.repo_name)}";
-                    
-                    var checkbox_checker = function(e){
-                        var clicked_cb = e.currentTarget;
-                        var checked_checkboxes = [];
-                        for (pos in checkboxes){
-                            if(checkboxes[pos].checked){
-                                checked_checkboxes.push(checkboxes[pos]);
-                            }
+                //Monitor range checkboxes and build a link to changesets
+                //ranges
+                var checkboxes = YUD.getElementsByClassName('changeset_range');
+                var url_tmpl = "${h.url('changeset_home',repo_name=c.repo_name,revision='__REVRANGE__')}";
+                var pr_tmpl = "${h.url('pullrequest_home',repo_name=c.repo_name)}";
+                        
+                var checkbox_checker = function(e){
+                    var clicked_cb = e.currentTarget;
+                    var checked_checkboxes = [];
+                    for (pos in checkboxes){
+                        if(checkboxes[pos].checked){
+                            checked_checkboxes.push(checkboxes[pos]);
                         }
-                        if(YUD.get('open_new_pr')){
-                          if(checked_checkboxes.length>0){
+                    }
+                    if(YUD.get('open_new_pr')){
+                        if(checked_checkboxes.length>0){
                             // modify open pull request to show we have selected cs
                             YUD.get('open_new_pr').innerHTML = _TM['Open new pull request for selected changesets'];
-                          }else{
+                        }else{
                             YUD.get('open_new_pr').innerHTML = _TM['Open new pull request'];
-                          }
-                        }
-
-                        if(checked_checkboxes.length>0){
-                            var rev_end = checked_checkboxes[0].name;
-                            var rev_start = checked_checkboxes[checked_checkboxes.length-1].name;
-                            var url = url_tmpl.replace('__REVRANGE__',
-                                    rev_start+'...'+rev_end);
-
-                            var link = (rev_start == rev_end)
-                                ? _TM['Show selected change __S']
-                                : _TM['Show selected changes __S -> __E'];
-
-                            link = link.replace('__S',rev_start.substr(0,6));
-                            link = link.replace('__E',rev_end.substr(0,6));
-                            YUD.get('rev_range_container').href = url;
-                            YUD.get('rev_range_container').innerHTML = link;
-                            YUD.setStyle('rev_range_container','display','');
-                            YUD.setStyle('rev_range_clear','display','');
-
-                            YUD.get('open_new_pr').href = pr_tmpl + '?rev_start={0}&rev_end={1}'.format(rev_start,rev_end);
-
-                        }
-                        else{
-                            YUD.setStyle('rev_range_container','display','none');
-                            YUD.setStyle('rev_range_clear','display','none');
-                        }
-                    };
-                    YUE.onDOMReady(checkbox_checker);
-                    YUE.on(checkboxes,'click', checkbox_checker);
-
-                    YUE.on('rev_range_clear','click',function(e){
-                        for (var i=0; i<checkboxes.length; i++){
-                            var cb = checkboxes[i];
-                            cb.checked = false;
-                        }
-                        YUE.preventDefault(e);
-                    })
-                    var msgs = YUQ('.message');
-                    // get first element height
-                    var el = YUQ('#graph_content .container')[0];
-                    var row_h = el.clientHeight;
-                    for(var i=0;i<msgs.length;i++){
-                        var m = msgs[i];
-
-                        var h = m.clientHeight;
-                        var pad = YUD.getStyle(m,'padding');
-                        if(h > row_h){
-                            var offset = row_h - (h+12);
-                            YUD.setStyle(m.nextElementSibling,'display','block');
-                            YUD.setStyle(m.nextElementSibling,'margin-top',offset+'px');
-                        };
-                    }
-                    YUE.on(YUQ('.expand'),'click',function(e){
-                        var elem = e.currentTarget.parentNode.parentNode;
-                        YUD.setStyle(e.currentTarget,'display','none');
-                        YUD.setStyle(elem,'height','auto');
-
-                        //redraw the graph, line_count and jsdata are global vars
-                        set_canvas(100);
-
-                        var r = new BranchRenderer();
-                        r.render(jsdata,100,line_count);
-
-                    })
-
-                    // Fetch changeset details
-                    YUE.on(YUD.getElementsByClassName('changed_total'),'click',function(e){
-                        var id = e.currentTarget.id;
-                        var url = "${h.url('changelog_details',repo_name=c.repo_name,cs='__CS__')}";
-                        var url = url.replace('__CS__',id.replace('changed_total_',''));
-                        ypjax(url,id,function(){tooltip_activate()});
-                    });
-
-                    // change branch filter
-                    YUE.on(YUD.get('branch_filter'),'change',function(e){
-                        var selected_branch = e.currentTarget.options[e.currentTarget.selectedIndex].value;
-                        var url_main = "${h.url('changelog_home',repo_name=c.repo_name)}";
-                        var url = "${h.url('changelog_home',repo_name=c.repo_name,branch='__BRANCH__')}";
-                        var url = url.replace('__BRANCH__',selected_branch);
-                        if(selected_branch != ''){
-                            window.location = url;
-                        }else{
-                            window.location = url_main;
-                        }
-
-                    });
-
-                    function set_canvas(width) {
-                        var c = document.getElementById('graph_nodes');
-                        var t = document.getElementById('graph_content');
-                        canvas = document.getElementById('graph_canvas');
-                        var div_h = t.clientHeight;
-                        c.style.height=div_h+'px';
-                        canvas.setAttribute('height',div_h);
-                        c.style.height=width+'px';
-                        canvas.setAttribute('width',width);
-                    };
-                    var heads = 1;
-                    var line_count = 0;
-                    var jsdata = ${c.jsdata|n};
-
-                    for (var i=0;i<jsdata.length;i++) {
-                        var in_l = jsdata[i][2];
-                        for (var j in in_l) {
-                            var m = in_l[j][1];
-                            if (m > line_count)
-                                line_count = m;
                         }
                     }
+
+                    if(checked_checkboxes.length>0){
+                        var rev_end = checked_checkboxes[0].name;
+                        var rev_start = checked_checkboxes[checked_checkboxes.length-1].name;
+                        var url = url_tmpl.replace('__REVRANGE__',
+                                rev_start+'...'+rev_end);
+
+                        var link = (rev_start == rev_end)
+                            ? _TM['Show selected change __S']
+                            : _TM['Show selected changes __S -> __E'];
+
+                        link = link.replace('__S',rev_start.substr(0,6));
+                        link = link.replace('__E',rev_end.substr(0,6));
+                        YUD.get('rev_range_container').href = url;
+                        YUD.get('rev_range_container').innerHTML = link;
+                        YUD.setStyle('rev_range_container','display','');
+                        YUD.setStyle('rev_range_clear','display','');
+
+                        YUD.get('open_new_pr').href = pr_tmpl + '?rev_start={0}&rev_end={1}'.format(rev_start,rev_end);
+
+                    } else{
+                        YUD.setStyle('rev_range_container','display','none');
+                        YUD.setStyle('rev_range_clear','display','none');
+                    }
+                };
+                YUE.onDOMReady(checkbox_checker);
+                YUE.on(checkboxes,'click', checkbox_checker);
+
+                YUE.on('rev_range_clear','click',function(e){
+                    for (var i=0; i<checkboxes.length; i++){
+                        var cb = checkboxes[i];
+                        cb.checked = false;
+                    }
+                    YUE.preventDefault(e);
+                });
+
+                var msgs = YUQ('.message');
+                // get first element height
+                var el = YUQ('#graph_content .container')[0];
+                var row_h = el.clientHeight;
+                for(var i=0;i<msgs.length;i++){
+                    var m = msgs[i];
+
+                    var h = m.clientHeight;
+                    var pad = YUD.getStyle(m,'padding');
+                    if(h > row_h){
+                        var offset = row_h - (h+12);
+                        YUD.setStyle(m.nextElementSibling,'display','block');
+                        YUD.setStyle(m.nextElementSibling,'margin-top',offset+'px');
+                    };
+                }
+                YUE.on(YUQ('.expand'),'click',function(e){
+                    var elem = e.currentTarget.parentNode.parentNode;
+                    YUD.setStyle(e.currentTarget,'display','none');
+                    YUD.setStyle(elem,'height','auto');
+
+                    //redraw the graph, line_count and jsdata are global vars
                     set_canvas(100);
 
                     var r = new BranchRenderer();
                     r.render(jsdata,100,line_count);
 
                 });
-            </script>
+
+                // Fetch changeset details
+                YUE.on(YUD.getElementsByClassName('changed_total'),'click',function(e){
+                    var id = e.currentTarget.id;
+                    var url = "${h.url('changelog_details',repo_name=c.repo_name,cs='__CS__')}";
+                    var url = url.replace('__CS__',id.replace('changed_total_',''));
+                    ypjax(url,id,function(){tooltip_activate()});
+                });
+
+                // change branch filter
+                YUE.on(YUD.get('branch_filter'),'change',function(e){
+                    var selected_branch = e.currentTarget.options[e.currentTarget.selectedIndex].value;
+                    var url_main = "${h.url('changelog_home',repo_name=c.repo_name)}";
+                    var url = "${h.url('changelog_home',repo_name=c.repo_name,branch='__BRANCH__')}";
+                    var url = url.replace('__BRANCH__',selected_branch);
+                    if(selected_branch != ''){
+                        window.location = url;
+                    }else{
+                        window.location = url_main;
+                    }
+
+                });
+
+                function set_canvas(width) {
+                    var c = document.getElementById('graph_nodes');
+                    var t = document.getElementById('graph_content');
+                    canvas = document.getElementById('graph_canvas');
+                    var div_h = t.clientHeight;
+                    //c.style.height=div_h+'px';
+                    canvas.setAttribute('height',div_h);
+                    //c.style.height=width+'px';
+                    canvas.setAttribute('width',width);
+                };
+                var heads = 1;
+                var line_count = 0;
+                var jsdata = ${c.jsdata|n};
+
+                for (var i=0;i<jsdata.length;i++) {
+                    var in_l = jsdata[i][2];
+                    for (var j in in_l) {
+                        var m = in_l[j][1];
+                        if (m > line_count)
+                            line_count = m;
+                    }
+                }
+                set_canvas(100);
+
+                var r = new BranchRenderer();
+                r.render(jsdata,100,line_count);
+
+            });
+        </script>
         %else:
             ${_('There are no changes yet')}
         %endif