changeset 1605:df59c0503636 beta

implements #215 Repository view uses a README (text/markdown + rst)
author Marcin Kuzminski <marcin@python-works.com>
date Thu, 27 Oct 2011 03:26:02 +0200
parents bb3c2111bf92
children a30689fc4f61
files rhodecode/controllers/summary.py rhodecode/lib/__init__.py rhodecode/public/css/style.css rhodecode/templates/summary/summary.html setup.py
diffstat 5 files changed, 620 insertions(+), 489 deletions(-) [+]
line wrap: on
line diff
--- a/rhodecode/controllers/summary.py	Thu Oct 27 03:21:05 2011 +0200
+++ b/rhodecode/controllers/summary.py	Thu Oct 27 03:26:02 2011 +0200
@@ -23,23 +23,25 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
+import traceback
 import calendar
 import logging
 from time import mktime
-from datetime import datetime, timedelta, date
+from datetime import timedelta, date
+from itertools import product
 
-from vcs.exceptions import ChangesetError
+from vcs.exceptions import ChangesetError, EmptyRepositoryError, \
+    NodeDoesNotExistError
 
 from pylons import tmpl_context as c, request, url
 from pylons.i18n.translation import _
 
-from rhodecode.model.db import Statistics, Repository
-from rhodecode.model.repo import RepoModel
-
+from rhodecode.model.db import Statistics
+from rhodecode.lib import ALL_READMES, ALL_EXTS
 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
 from rhodecode.lib.base import BaseRepoController, render
 from rhodecode.lib.utils import EmptyChangeset
-
+from rhodecode.lib.markup_renderer import MarkupRenderer
 from rhodecode.lib.celerylib import run_task
 from rhodecode.lib.celerylib.tasks import get_commits_stats, \
     LANGUAGES_EXTENSIONS_MAP
@@ -48,6 +50,9 @@
 
 log = logging.getLogger(__name__)
 
+README_FILES = [''.join([x[0][0], x[1][0]]) for x in 
+                    sorted(list(product(ALL_READMES, ALL_EXTS)),
+                           key=lambda y:y[0][1] + y[1][1])]
 
 class SummaryController(BaseRepoController):
 
@@ -161,8 +166,33 @@
         if c.enable_downloads:
             c.download_options = self._get_download_links(c.rhodecode_repo)
 
+        c.readme_data,c.readme_file = self.__get_readme_data()
         return render('summary/summary.html')
 
+    def __get_readme_data(self):
+        readme_data = None
+        readme_file = None
+        
+        try:
+            cs = c.rhodecode_repo.get_changeset('tip')
+            renderer = MarkupRenderer()
+            for f in README_FILES:
+                try:
+                    readme = cs.get_node(f)
+                    readme_file = f
+                    readme_data = renderer.render(readme.content, f)
+                    break
+                except NodeDoesNotExistError:
+                    continue
+        except ChangesetError:
+            pass
+        except EmptyRepositoryError:
+            pass
+        except Exception:
+            log.error(traceback.format_exc())        
+
+        return readme_data, readme_file
+
     def _get_download_links(self, repo):
 
         download_l = []
@@ -181,3 +211,4 @@
         download_l.append(tags_group)
 
         return download_l
+
--- a/rhodecode/lib/__init__.py	Thu Oct 27 03:21:05 2011 +0200
+++ b/rhodecode/lib/__init__.py	Thu Oct 27 03:26:02 2011 +0200
@@ -66,6 +66,34 @@
 
 LANGUAGES_EXTENSIONS_MAP.update(ADDITIONAL_MAPPINGS)
 
+# list of readme files to search in file tree and display in summary
+# attached weights defines the search  order lower is first
+ALL_READMES = [
+    ('readme', 0), ('README', 0), ('Readme', 0),
+    ('doc/readme', 1), ('doc/README', 1), ('doc/Readme', 1),
+    ('Docs/readme', 2), ('Docs/README', 2), ('Docs/Readme', 2),
+    ('DOCS/readme', 2), ('DOCS/README', 2), ('DOCS/Readme', 2),
+    ('docs/readme', 2), ('docs/README', 2), ('docs/Readme', 2),
+]
+
+# extension together with weights to search lower is first
+RST_EXTS = [
+    ('', 0), ('.rst', 1),('.rest', 1),
+    ('.RST', 2) ,('.REST', 2), 
+    ('.txt', 3), ('.TXT', 3) 
+]
+
+MARKDOWN_EXTS = [
+    ('.md', 1), ('.MD', 1),
+    ('.mkdn', 2), ('.MKDN', 2),
+    ('.mdown', 3), ('.MDOWN', 3),
+    ('.markdown', 4), ('.MARKDOWN', 4)
+]
+
+PLAIN_EXTS = [('.text', 2),('.TEXT', 2)]
+
+ALL_EXTS = MARKDOWN_EXTS + RST_EXTS + PLAIN_EXTS
+
 
 def str2bool(_str):
     """
--- a/rhodecode/public/css/style.css	Thu Oct 27 03:21:05 2011 +0200
+++ b/rhodecode/public/css/style.css	Thu Oct 27 03:26:02 2011 +0200
@@ -3011,4 +3011,96 @@
 	border: 0px solid #545454;
 	color: #AAAAAA;
 	padding-left: 3px;
-}
\ No newline at end of file
+}
+
+/*README STYLE*/
+
+div.readme {
+	padding:0px;
+}
+
+div.readme h2 {
+    font-weight: normal;
+}
+
+div.readme .readme_box {
+    background-color: #fafafa;
+}
+
+div.readme .readme_box {
+clear:both;
+overflow:hidden;
+margin:0;
+padding:0 20px 10px;
+}
+
+div.readme .readme_box h1, div.readme .readme_box h2, div.readme .readme_box h3, div.readme .readme_box h4, div.readme .readme_box h5, div.readme .readme_box h6 {
+border-bottom: 0 !important;
+margin: 0 !important;
+padding: 0 !important;
+line-height: 1.5em !important;
+}
+
+
+div.readme .readme_box h1:first-child {
+padding-top: .25em !important;
+}
+
+div.readme .readme_box h2, div.readme .readme_box h3 {
+margin: 1em 0 !important;
+}
+
+div.readme .readme_box h2 {
+margin-top: 1.5em !important;
+border-top: 4px solid #e0e0e0 !important;
+padding-top: .5em !important;
+}
+
+div.readme .readme_box p {
+color: black !important;
+margin: 1em 0 !important;
+line-height: 1.5em !important;
+}
+
+div.readme .readme_box ul {
+list-style: disc !important;
+margin: 1em 0 1em 2em !important;
+}
+
+div.readme .readme_box ol {
+list-style: decimal;
+margin: 1em 0 1em 2em !important;
+}
+
+div.readme .readme_box pre, code {
+font: 12px "Bitstream Vera Sans Mono","Courier",monospace;
+}
+
+div.readme .readme_box code {
+    font-size: 12px !important;
+    background-color: ghostWhite !important;
+    color: #444 !important;
+    padding: 0 .2em !important;
+    border: 1px solid #dedede !important;
+}
+
+div.readme .readme_box pre code {
+padding: 0 !important;
+font-size: 12px !important;
+background-color: #eee !important;
+border: none !important;
+}
+
+div.readme .readme_box pre {
+margin: 1em 0;
+font-size: 12px;
+background-color: #eee;
+border: 1px solid #ddd;
+padding: 5px;
+color: #444;
+overflow: auto;
+-webkit-box-shadow: rgba(0,0,0,0.07) 0 1px 2px inset;
+-webkit-border-radius: 3px;
+-moz-border-radius: 3px;
+border-radius: 3px;
+}
--- a/rhodecode/templates/summary/summary.html	Thu Oct 27 03:21:05 2011 +0200
+++ b/rhodecode/templates/summary/summary.html	Thu Oct 27 03:26:02 2011 +0200
@@ -64,29 +64,22 @@
                   ##FORK
 		          %if c.dbrepo.fork:
 	            	<div style="margin-top:5px;clear:both"">
-	            	<a href="${h.url('summary_home',repo_name=c.dbrepo.fork.repo_name)}">
-	            	<img class="icon" alt="${_('public')}"
-	            	title="${_('Fork of')} ${c.dbrepo.fork.repo_name}" 
-	            	src="${h.url('/images/icons/arrow_divide.png')}"/>
-	            	${_('Fork of')} ${c.dbrepo.fork.repo_name}
+	            	<a href="${h.url('summary_home',repo_name=c.dbrepo.fork.repo_name)}"><img class="icon" alt="${_('public')}" title="${_('Fork of')} ${c.dbrepo.fork.repo_name}" src="${h.url('/images/icons/arrow_divide.png')}"/>
+	            	    ${_('Fork of')} ${c.dbrepo.fork.repo_name}
 	            	</a>
 	            	</div>
 		          %endif
 		          ##REMOTE
 				  %if c.dbrepo.clone_uri:
                     <div style="margin-top:5px;clear:both">
-                    <a href="${h.url(str(h.hide_credentials(c.dbrepo.clone_uri)))}">
-                    <img class="icon" alt="${_('remote clone')}"
-                    title="${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}" 
-                    src="${h.url('/images/icons/connect.png')}"/>
-                    ${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}
+                    <a href="${h.url(str(h.hide_credentials(c.dbrepo.clone_uri)))}"><img class="icon" alt="${_('remote clone')}" title="${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}" src="${h.url('/images/icons/connect.png')}"/>
+                        ${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}
                     </a>
                     </div>					
 				  %endif		            		      
 			  </div>
 			 </div>
 			
-			
 			 <div class="field">
 			  <div class="label">
 			      <label>${_('Description')}:</label>
@@ -94,7 +87,6 @@
 			  <div class="input-short desc">${h.urlify_text(c.dbrepo.description)}</div>
 			 </div>
 			
-			
 			 <div class="field">
 			  <div class="label">
 			      <label>${_('Contact')}:</label>
@@ -119,7 +111,6 @@
 			      <span class="tooltip" title="${c.rhodecode_repo.last_change}">
 			      ${h.age(c.rhodecode_repo.last_change)}</span><br/>
 			      ${_('by')} ${h.get_changeset_safe(c.rhodecode_repo,'tip').author} 
-			      
 			  </div>
 			 </div>
 			
@@ -187,123 +178,6 @@
 			 </div>				 			 			 
 	  </div>		 
 	</div>
-  	<script type="text/javascript">
-	  	YUE.onDOMReady(function(e){
-	  	    id = 'clone_url';
-	  	    YUE.on(id,'click',function(e){
-	  	    	if(YUD.hasClass(id,'selected')){
-	  	    		return
-	  	    	}
-	  	    	else{
-	                YUD.addClass(id,'selected');
-	                YUD.get(id).select();	  	    		
-	  	    	}
-
-	  	    })
-	  	})
-  		var data = ${c.trending_languages|n};
-  		var total = 0;
-  		var no_data = true;
-  		for (k in data){
-  		    total += data[k].count;
-  		    no_data = false;
-  		} 
-		var tbl = document.createElement('table');
-		tbl.setAttribute('class','trending_language_tbl');
-		var cnt = 0;
-  		for (k in data){
-  			cnt += 1;
-  			var hide = cnt>2;
-	  		var tr = document.createElement('tr');
-	  		if (hide){
-	  			tr.setAttribute('style','display:none');
-	  			tr.setAttribute('class','stats_hidden');
-	  		}
-	  		var percentage = Math.round((data[k].count/total*100),2);
-			var value = data[k].count;
-	  		var td1 = document.createElement('td');
-	  		td1.width = 150;
-	  		var trending_language_label = document.createElement('div');
-	  		trending_language_label.innerHTML = data[k].desc+" ("+k+")";
-	  		td1.appendChild(trending_language_label);
-
-	  		var td2 = document.createElement('td');
-	  		td2.setAttribute('style','padding-right:14px !important');
-  		    var trending_language = document.createElement('div');
-  		    var nr_files = value+" ${_('files')}";
-  		    
-  		    trending_language.title = k+" "+nr_files;
-  		    
-  		    if (percentage>22){
-  		    	trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"% "+nr_files+ "</b>";	
-  		    }
-  		    else{
-  		    	trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"%</b>";
-  		    }
-  		    
-  		    trending_language.setAttribute("class", 'trending_language top-right-rounded-corner bottom-right-rounded-corner');
-  		    trending_language.style.width=percentage+"%";
-			td2.appendChild(trending_language);
-			
-			tr.appendChild(td1);
-			tr.appendChild(td2);
-  		    tbl.appendChild(tr);
-  		    if(cnt == 3){
-  		    	var show_more = document.createElement('tr');
-  		    	var td = document.createElement('td');
-  		    	lnk = document.createElement('a');
-  		    	
-  		    	lnk.href='#';
-  		    	lnk.innerHTML = "${_('show more')}";
-  		    	lnk.id='code_stats_show_more';
-  		        td.appendChild(lnk);
-  		        
-  		    	show_more.appendChild(td);
-  		    	show_more.appendChild(document.createElement('td'));
-  		    	tbl.appendChild(show_more);
-  		    }
-  		    
-  		}
-  		if(no_data){
-  			var tr = document.createElement('tr');
-  			var td1 = document.createElement('td');
-  			td1.innerHTML = "${c.no_data_msg}";
-  			tr.appendChild(td1);
-  			tbl.appendChild(tr);
-		}
-  		YUD.get('lang_stats').appendChild(tbl);
-  		YUE.on('code_stats_show_more','click',function(){
-  			l = YUD.getElementsByClassName('stats_hidden')
-  			for (e in l){
-  			    YUD.setStyle(l[e],'display','');
-  			};
-  			YUD.setStyle(YUD.get('code_stats_show_more'),
-  					'display','none');
-  		})
-  	
-             var tmpl_links = {}
-              %for cnt,archive in enumerate(c.rhodecode_repo._get_archives()):
-                tmpl_links['${archive['type']}'] = '${h.link_to(archive['type'],
-                     h.url('files_archive_home',repo_name=c.dbrepo.repo_name,
-                     fname='__CS__'+archive['extension'],subrepos='__SUB__'),class_="archive_icon")}';
-              %endfor
-              
-             YUE.on(['download_options','archive_subrepos'],'change',function(e){
-            	 var sm = YUD.get('download_options');
-                 var new_cs = sm.options[sm.selectedIndex];
-                 
-                 for(k in tmpl_links){
-                	 var s = YUD.get(k+'_link');
-                	 title_tmpl = "${_('Download %s as %s') % ('__CS_NAME__','__CS_EXT__')}";
-                	 s.title = title_tmpl.replace('__CS_NAME__',new_cs.text);
-                	 s.title = s.title.replace('__CS_EXT__',k);
-                	 var url = tmpl_links[k].replace('__CS__',new_cs.value);
-                	 var subrepos = YUD.get('archive_subrepos').checked
-                	 url = url.replace('__SUB__',subrepos);
-                	 s.innerHTML = url 
-                 }
-             });
-  	</script>    				
 </div>
         
 <div class="box box-right"  style="min-height:455px">
@@ -319,7 +193,6 @@
            %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
                 ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-button-small")}
            %endif         
-           
         %else:
             ${_('Loaded in')} ${c.stats_percentage} %
         %endif
@@ -331,333 +204,9 @@
     	<div id="legend_data" style="clear:both;margin-top:10px;">
 	    	<div id="legend_container"></div>
 	    	<div id="legend_choices">
-				<table id="legend_choices_tables" style="font-size:smaller;color:#545454"></table>
+				<table id="legend_choices_tables" class="noborder" style="font-size:smaller;color:#545454"></table>
 	    	</div>
     	</div>
-		<script type="text/javascript">
-		/**
-		 * Plots summary graph
-		 *
-		 * @class SummaryPlot
-		 * @param {from} initial from for detailed graph
-		 * @param {to} initial to for detailed graph
-		 * @param {dataset}
-		 * @param {overview_dataset}
-		 */
-		function SummaryPlot(from,to,dataset,overview_dataset) {
-			var initial_ranges = {
-			    "xaxis":{
-				    "from":from,
-				   	"to":to,
-				},
-			};
-		    var dataset = dataset;
-		    var overview_dataset = [overview_dataset];
-		    var choiceContainer = YUD.get("legend_choices");
-		    var choiceContainerTable = YUD.get("legend_choices_tables");
-		    var plotContainer = YUD.get('commit_history');
-		    var overviewContainer = YUD.get('overview');
-		    
-		    var plot_options = {
-				bars: {show:true,align:'center',lineWidth:4},
-				legend: {show:true, container:"legend_container"},
-				points: {show:true,radius:0,fill:false},
-				yaxis: {tickDecimals:0,},
-				xaxis: {
-					mode: "time", 
-					timeformat: "%d/%m",
-				    min:from,
-				    max:to,	
-				}, 
-				grid: {
-					hoverable: true, 
-				    clickable: true,
-				    autoHighlight:true,
-				    color: "#999"
-				},
-				//selection: {mode: "x"}
-		    };
-		    var overview_options = {
-				legend:{show:false},
-			    bars: {show:true,barWidth: 2,},
-			    shadowSize: 0,
-			    xaxis: {mode: "time", timeformat: "%d/%m/%y",},
-			    yaxis: {ticks: 3, min: 0,tickDecimals:0,},
-			    grid: {color: "#999",},
-			    selection: {mode: "x"}
-			};
-
-			/**
-			*get dummy data needed in few places
-			*/
-		    function getDummyData(label){
-		    	return {"label":label,
-               	 "data":[{"time":0,
-               		 "commits":0,
-	                     "added":0,
-	                     "changed":0,
-	                     "removed":0,
-                    }],
-                    "schema":["commits"],
-                    "color":'#ffffff',
-           		}
-			}
-			
-		    /**
-		     * generate checkboxes accordindly to data
-		     * @param keys
-		     * @returns
-		     */
-		    function generateCheckboxes(data) {
-			    //append checkboxes
-			    var i = 0;
-			    choiceContainerTable.innerHTML = '';
-			    for(var pos in data) {
-			    	
-			    	data[pos].color = i;
-			        i++;
-			        if(data[pos].label != ''){
-				        choiceContainerTable.innerHTML += '<tr><td>'+
-				        '<input type="checkbox" name="' + data[pos].label +'" checked="checked" />'
-				        +data[pos].label+
-				        '</td></tr>';
-			        }
-			    }	
-		    }
-		    
-		    /**
-		     * ToolTip show
-		     */
-		    function showTooltip(x, y, contents) {
-		        var div=document.getElementById('tooltip');
-		        if(!div) {
-		            div = document.createElement('div');
-		            div.id="tooltip";
-		            div.style.position="absolute";
-		            div.style.border='1px solid #fdd';
-		            div.style.padding='2px';
-		            div.style.backgroundColor='#fee';
-		            document.body.appendChild(div);
-		        }
-		        YUD.setStyle(div, 'opacity', 0);
-		        div.innerHTML = contents;
-		        div.style.top=(y + 5) + "px";
-		        div.style.left=(x + 5) + "px";
-
-		        var anim = new YAHOO.util.Anim(div, {opacity: {to: 0.8}}, 0.2);
-		        anim.animate();
-		    }
-		    
-			/**
-			 * This function will detect if selected period has some changesets 
-			   for this user if it does this data is then pushed for displaying
-			   Additionally it will only display users that are selected by the checkbox
-			*/
-		    function getDataAccordingToRanges(ranges) {
-		    	
-		        var data = [];
-		        var new_dataset = {};
-		        var keys = [];
-		        var max_commits = 0;
-				for(var key in dataset){
-					
-		            for(var ds in dataset[key].data){
-			            commit_data = dataset[key].data[ds];
-			            if (commit_data.time >= ranges.xaxis.from && commit_data.time <= ranges.xaxis.to){
-
-			            	if(new_dataset[key] === undefined){
-			            		new_dataset[key] = {data:[],schema:["commits"],label:key};	
-			            	}
-			            	new_dataset[key].data.push(commit_data);
-					    }
-				    }
-		            if (new_dataset[key] !== undefined){
-		            	data.push(new_dataset[key]);	
-		            }
-				}
-
-				if (data.length > 0){
-					return data;	
-				}
-				else{
-					//just return dummy data for graph to plot itself
-					return [getDummyData('')];	
-				}
-		    }
-		    
-			/**
-			* redraw using new checkbox data
-			*/
-		    function plotchoiced(e,args){
-			    var cur_data = args[0];
-			    var cur_ranges = args[1];
-		    	
-				var new_data = [];
-		    	var inputs = choiceContainer.getElementsByTagName("input");
-
-		    	//show only checked labels
-		        for(var i=0; i<inputs.length; i++) {
-		            var checkbox_key = inputs[i].name;
-		            
-	                if(inputs[i].checked){
-						for(var d in cur_data){
-							if(cur_data[d].label == checkbox_key){
-								new_data.push(cur_data[d]);
-							}
-						}			                
-	    	        }
-	                else{
-		                //push dummy data to not hide the label
-						new_data.push(getDummyData(checkbox_key));
-			        }
-		        }
-					        
-		    	var new_options = YAHOO.lang.merge(plot_options, {
-		            xaxis: { 
-		  	      		min: cur_ranges.xaxis.from, 
-		  	      		max: cur_ranges.xaxis.to,
-		  	      		mode:"time",
-		  	      		timeformat: "%d/%m",
-		        	},
-		    	});
-		    	if (!new_data){
-					new_data = [[0,1]];
-				}
-		    	// do the zooming
-		       plot = YAHOO.widget.Flot(plotContainer, new_data, new_options);
-		       
-		       plot.subscribe("plotselected", plotselected);
-	
-		       //resubscribe plothover
-		       plot.subscribe("plothover", plothover);
-		        
-		       // don't fire event on the overview to prevent eternal loop 
-		       overview.setSelection(cur_ranges, true);
-	
-		    }
-		    
-			/**
-		     * plot only selected items from overview
-		     * @param ranges
-		     * @returns
-		     */
-		    function plotselected(ranges,cur_data) {
-			    //updates the data for new plot
-	    		var data = getDataAccordingToRanges(ranges);
-	    		generateCheckboxes(data);
-	    		
-		    	var new_options = YAHOO.lang.merge(plot_options, {
-		            xaxis: { 
-		  	      		min: ranges.xaxis.from, 
-		  	      		max: ranges.xaxis.to,
-		  	      		mode:"time",
-		  	      		timeformat: "%d/%m",
-		        	},
-		    	});
-		    	// do the zooming 
-		        plot = YAHOO.widget.Flot(plotContainer, data, new_options);
-
-		        plot.subscribe("plotselected", plotselected);
-
-		        //resubscribe plothover
-		        plot.subscribe("plothover", plothover);
-		        
-		        // don't fire event on the overview to prevent eternal loop
-		        overview.setSelection(ranges, true);
-
-		        //resubscribe choiced 
-		        YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, ranges]);
-		    }
-		    
-		    var previousPoint = null;
-
-			function plothover(o) {
-		        var pos = o.pos;
-		        var item = o.item;
-		        
-		        //YUD.get("x").innerHTML = pos.x.toFixed(2);
-		        //YUD.get("y").innerHTML = pos.y.toFixed(2);
-		        if (item) {
-		            if (previousPoint != item.datapoint) {
-		                previousPoint = item.datapoint;
-		                
-		                var tooltip = YUD.get("tooltip");
-		                if(tooltip) {
-		                	  tooltip.parentNode.removeChild(tooltip);
-		                }
-		                var x = item.datapoint.x.toFixed(2);
-		                var y = item.datapoint.y.toFixed(2);
-						
-		                if (!item.series.label){
-		                    item.series.label = 'commits';
-		                }
-		                var d = new Date(x*1000);
-		                var fd = d.toDateString()
-		                var nr_commits = parseInt(y);
-		                
-		                var cur_data = dataset[item.series.label].data[item.dataIndex];
-		                var added = cur_data.added;
-		                var changed = cur_data.changed;
-		                var removed = cur_data.removed;
-		                
-		                var nr_commits_suffix = " ${_('commits')} ";
-		                var added_suffix = " ${_('files added')} ";
-		                var changed_suffix = " ${_('files changed')} ";
-		                var removed_suffix = " ${_('files removed')} ";
-
-		                
-		                if(nr_commits == 1){nr_commits_suffix = " ${_('commit')} ";}
-						if(added==1){added_suffix=" ${_('file added')} ";}
-						if(changed==1){changed_suffix=" ${_('file changed')} ";}
-						if(removed==1){removed_suffix=" ${_('file removed')} ";}
-										                
-		                showTooltip(item.pageX, item.pageY, item.series.label + " on " + fd
-								 +'<br/>'+
-		                         nr_commits + nr_commits_suffix+'<br/>'+
-		                         added + added_suffix +'<br/>'+
-		                         changed + changed_suffix + '<br/>'+
-		                         removed + removed_suffix + '<br/>');
-		            }
-		        }
-		        else {
-		        	  var tooltip = YUD.get("tooltip");
-		        	  
-			          if(tooltip) {
-			                tooltip.parentNode.removeChild(tooltip);
-			          }
-		            previousPoint = null;
-		        }
-		    }
-			
-		    /**
-		     * MAIN EXECUTION
-		     */
-			
-			var data = getDataAccordingToRanges(initial_ranges);    
-			generateCheckboxes(data);
-			
-		    //main plot 
-		    var plot = YAHOO.widget.Flot(plotContainer,data,plot_options);
-		    
-			//overview 
-			var overview = YAHOO.widget.Flot(overviewContainer, 
-					overview_dataset, overview_options);
-			
-			//show initial selection on overview 
-			overview.setSelection(initial_ranges);    
-			
-		    plot.subscribe("plotselected", plotselected);
-		    plot.subscribe("plothover", plothover)
-		    
-		    overview.subscribe("plotselected", function (ranges) {
-		        plot.setSelection(ranges);
-		    });		
-
-		    YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, initial_ranges]);
-		}
-			SummaryPlot(${c.ts_min},${c.ts_max},${c.commit_data|n},${c.overview_data|n});		
-		</script>
-
     </div>
 </div>    
 
@@ -669,32 +218,461 @@
         <div id="shortlog_data">
             <%include file='../shortlog/shortlog_data.html'/>
         </div>
-        ##%if c.repo_changesets:
-        ##	${h.link_to(_('show more'),h.url('changelog_home',repo_name=c.repo_name))}
-        ##%endif
+    </div>
+</div>
+
+%if c.readme_data:
+<div class="box" style="background-color: #FAFAFA">    
+    <div class="title">
+        <div class="breadcrumbs"><a href="${h.url('files_home',repo_name=c.repo_name,revision='tip',f_path=c.readme_file)}">${c.readme_file}</a></div>
+    </div>    
+    <div class="readme">
+      <div class="readme_box">
+        ${c.readme_data|n}
+      </div>
     </div>
 </div>
-<div class="box">    
-    <div class="title">
-        <div class="breadcrumbs">${h.link_to(_('Tags'),h.url('tags_home',repo_name=c.repo_name))}</div>
-    </div>    
-    <div class="table">
-        <%include file='../tags/tags_data.html'/>
-        %if c.repo_changesets:
-        	${h.link_to(_('show more'),h.url('tags_home',repo_name=c.repo_name))}
-        %endif
-    </div>
-</div>
-<div class="box">
-    <div class="title">
-        <div class="breadcrumbs">${h.link_to(_('Branches'),h.url('branches_home',repo_name=c.repo_name))}</div>
-    </div>    
-    <div class="table">
-        <%include file='../branches/branches_data.html'/>
-        %if c.repo_changesets:
-        	${h.link_to(_('show more'),h.url('branches_home',repo_name=c.repo_name))}
-        %endif
-    </div>      
-</div> 
+%endif
+
+<script type="text/javascript">
+        YUE.onDOMReady(function(e){
+            id = 'clone_url';
+            YUE.on(id,'click',function(e){
+                if(YUD.hasClass(id,'selected')){
+                    return
+                }
+                else{
+                    YUD.addClass(id,'selected');
+                    YUD.get(id).select();                   
+                }
+
+            })
+        })
+        var data = ${c.trending_languages|n};
+        var total = 0;
+        var no_data = true;
+        for (k in data){
+            total += data[k].count;
+            no_data = false;
+        } 
+        var tbl = document.createElement('table');
+        tbl.setAttribute('class','trending_language_tbl');
+        var cnt = 0;
+        for (k in data){
+            cnt += 1;
+            var hide = cnt>2;
+            var tr = document.createElement('tr');
+            if (hide){
+                tr.setAttribute('style','display:none');
+                tr.setAttribute('class','stats_hidden');
+            }
+            var percentage = Math.round((data[k].count/total*100),2);
+            var value = data[k].count;
+            var td1 = document.createElement('td');
+            td1.width = 150;
+            var trending_language_label = document.createElement('div');
+            trending_language_label.innerHTML = data[k].desc+" ("+k+")";
+            td1.appendChild(trending_language_label);
+
+            var td2 = document.createElement('td');
+            td2.setAttribute('style','padding-right:14px !important');
+            var trending_language = document.createElement('div');
+            var nr_files = value+" ${_('files')}";
+            
+            trending_language.title = k+" "+nr_files;
+            
+            if (percentage>22){
+                trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"% "+nr_files+ "</b>";   
+            }
+            else{
+                trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"%</b>";
+            }
+            
+            trending_language.setAttribute("class", 'trending_language top-right-rounded-corner bottom-right-rounded-corner');
+            trending_language.style.width=percentage+"%";
+            td2.appendChild(trending_language);
+            
+            tr.appendChild(td1);
+            tr.appendChild(td2);
+            tbl.appendChild(tr);
+            if(cnt == 3){
+                var show_more = document.createElement('tr');
+                var td = document.createElement('td');
+                lnk = document.createElement('a');
+                
+                lnk.href='#';
+                lnk.innerHTML = "${_('show more')}";
+                lnk.id='code_stats_show_more';
+                td.appendChild(lnk);
+                
+                show_more.appendChild(td);
+                show_more.appendChild(document.createElement('td'));
+                tbl.appendChild(show_more);
+            }
+            
+        }
+        if(no_data){
+            var tr = document.createElement('tr');
+            var td1 = document.createElement('td');
+            td1.innerHTML = "${c.no_data_msg}";
+            tr.appendChild(td1);
+            tbl.appendChild(tr);
+        }
+        YUD.get('lang_stats').appendChild(tbl);
+        YUE.on('code_stats_show_more','click',function(){
+            l = YUD.getElementsByClassName('stats_hidden')
+            for (e in l){
+                YUD.setStyle(l[e],'display','');
+            };
+            YUD.setStyle(YUD.get('code_stats_show_more'),
+                    'display','none');
+        })
+    
+             var tmpl_links = {}
+              %for cnt,archive in enumerate(c.rhodecode_repo._get_archives()):
+                tmpl_links['${archive['type']}'] = '${h.link_to(archive['type'],
+                     h.url('files_archive_home',repo_name=c.dbrepo.repo_name,
+                     fname='__CS__'+archive['extension'],subrepos='__SUB__'),class_="archive_icon")}';
+              %endfor
+              
+             YUE.on(['download_options','archive_subrepos'],'change',function(e){
+                 var sm = YUD.get('download_options');
+                 var new_cs = sm.options[sm.selectedIndex];
+                 
+                 for(k in tmpl_links){
+                     var s = YUD.get(k+'_link');
+                     title_tmpl = "${_('Download %s as %s') % ('__CS_NAME__','__CS_EXT__')}";
+                     s.title = title_tmpl.replace('__CS_NAME__',new_cs.text);
+                     s.title = s.title.replace('__CS_EXT__',k);
+                     var url = tmpl_links[k].replace('__CS__',new_cs.value);
+                     var subrepos = YUD.get('archive_subrepos').checked
+                     url = url.replace('__SUB__',subrepos);
+                     s.innerHTML = url 
+                 }
+             });
+    </script>   
+<script type="text/javascript">
+        /**
+         * Plots summary graph
+         *
+         * @class SummaryPlot
+         * @param {from} initial from for detailed graph
+         * @param {to} initial to for detailed graph
+         * @param {dataset}
+         * @param {overview_dataset}
+         */
+        function SummaryPlot(from,to,dataset,overview_dataset) {
+            var initial_ranges = {
+                "xaxis":{
+                    "from":from,
+                    "to":to,
+                },
+            };
+            var dataset = dataset;
+            var overview_dataset = [overview_dataset];
+            var choiceContainer = YUD.get("legend_choices");
+            var choiceContainerTable = YUD.get("legend_choices_tables");
+            var plotContainer = YUD.get('commit_history');
+            var overviewContainer = YUD.get('overview');
+            
+            var plot_options = {
+                bars: {show:true,align:'center',lineWidth:4},
+                legend: {show:true, container:"legend_container"},
+                points: {show:true,radius:0,fill:false},
+                yaxis: {tickDecimals:0,},
+                xaxis: {
+                    mode: "time", 
+                    timeformat: "%d/%m",
+                    min:from,
+                    max:to, 
+                }, 
+                grid: {
+                    hoverable: true, 
+                    clickable: true,
+                    autoHighlight:true,
+                    color: "#999"
+                },
+                //selection: {mode: "x"}
+            };
+            var overview_options = {
+                legend:{show:false},
+                bars: {show:true,barWidth: 2,},
+                shadowSize: 0,
+                xaxis: {mode: "time", timeformat: "%d/%m/%y",},
+                yaxis: {ticks: 3, min: 0,tickDecimals:0,},
+                grid: {color: "#999",},
+                selection: {mode: "x"}
+            };
+
+            /**
+            *get dummy data needed in few places
+            */
+            function getDummyData(label){
+                return {"label":label,
+                 "data":[{"time":0,
+                     "commits":0,
+                         "added":0,
+                         "changed":0,
+                         "removed":0,
+                    }],
+                    "schema":["commits"],
+                    "color":'#ffffff',
+                }
+            }
+            
+            /**
+             * generate checkboxes accordindly to data
+             * @param keys
+             * @returns
+             */
+            function generateCheckboxes(data) {
+                //append checkboxes
+                var i = 0;
+                choiceContainerTable.innerHTML = '';
+                for(var pos in data) {
+                    
+                    data[pos].color = i;
+                    i++;
+                    if(data[pos].label != ''){
+                        choiceContainerTable.innerHTML += '<tr><td>'+
+                        '<input type="checkbox" name="' + data[pos].label +'" checked="checked" />'
+                        +data[pos].label+
+                        '</td></tr>';
+                    }
+                }   
+            }
+            
+            /**
+             * ToolTip show
+             */
+            function showTooltip(x, y, contents) {
+                var div=document.getElementById('tooltip');
+                if(!div) {
+                    div = document.createElement('div');
+                    div.id="tooltip";
+                    div.style.position="absolute";
+                    div.style.border='1px solid #fdd';
+                    div.style.padding='2px';
+                    div.style.backgroundColor='#fee';
+                    document.body.appendChild(div);
+                }
+                YUD.setStyle(div, 'opacity', 0);
+                div.innerHTML = contents;
+                div.style.top=(y + 5) + "px";
+                div.style.left=(x + 5) + "px";
+
+                var anim = new YAHOO.util.Anim(div, {opacity: {to: 0.8}}, 0.2);
+                anim.animate();
+            }
+            
+            /**
+             * This function will detect if selected period has some changesets 
+               for this user if it does this data is then pushed for displaying
+               Additionally it will only display users that are selected by the checkbox
+            */
+            function getDataAccordingToRanges(ranges) {
+                
+                var data = [];
+                var new_dataset = {};
+                var keys = [];
+                var max_commits = 0;
+                for(var key in dataset){
+                    
+                    for(var ds in dataset[key].data){
+                        commit_data = dataset[key].data[ds];
+                        if (commit_data.time >= ranges.xaxis.from && commit_data.time <= ranges.xaxis.to){
+
+                            if(new_dataset[key] === undefined){
+                                new_dataset[key] = {data:[],schema:["commits"],label:key};  
+                            }
+                            new_dataset[key].data.push(commit_data);
+                        }
+                    }
+                    if (new_dataset[key] !== undefined){
+                        data.push(new_dataset[key]);    
+                    }
+                }
+
+                if (data.length > 0){
+                    return data;    
+                }
+                else{
+                    //just return dummy data for graph to plot itself
+                    return [getDummyData('')];  
+                }
+            }
+            
+            /**
+            * redraw using new checkbox data
+            */
+            function plotchoiced(e,args){
+                var cur_data = args[0];
+                var cur_ranges = args[1];
+                
+                var new_data = [];
+                var inputs = choiceContainer.getElementsByTagName("input");
+
+                //show only checked labels
+                for(var i=0; i<inputs.length; i++) {
+                    var checkbox_key = inputs[i].name;
+                    
+                    if(inputs[i].checked){
+                        for(var d in cur_data){
+                            if(cur_data[d].label == checkbox_key){
+                                new_data.push(cur_data[d]);
+                            }
+                        }                           
+                    }
+                    else{
+                        //push dummy data to not hide the label
+                        new_data.push(getDummyData(checkbox_key));
+                    }
+                }
+                            
+                var new_options = YAHOO.lang.merge(plot_options, {
+                    xaxis: { 
+                        min: cur_ranges.xaxis.from, 
+                        max: cur_ranges.xaxis.to,
+                        mode:"time",
+                        timeformat: "%d/%m",
+                    },
+                });
+                if (!new_data){
+                    new_data = [[0,1]];
+                }
+                // do the zooming
+               plot = YAHOO.widget.Flot(plotContainer, new_data, new_options);
+               
+               plot.subscribe("plotselected", plotselected);
+    
+               //resubscribe plothover
+               plot.subscribe("plothover", plothover);
+                
+               // don't fire event on the overview to prevent eternal loop 
+               overview.setSelection(cur_ranges, true);
+    
+            }
+            
+            /**
+             * plot only selected items from overview
+             * @param ranges
+             * @returns
+             */
+            function plotselected(ranges,cur_data) {
+                //updates the data for new plot
+                var data = getDataAccordingToRanges(ranges);
+                generateCheckboxes(data);
+                
+                var new_options = YAHOO.lang.merge(plot_options, {
+                    xaxis: { 
+                        min: ranges.xaxis.from, 
+                        max: ranges.xaxis.to,
+                        mode:"time",
+                        timeformat: "%d/%m",
+                    },
+                });
+                // do the zooming 
+                plot = YAHOO.widget.Flot(plotContainer, data, new_options);
+
+                plot.subscribe("plotselected", plotselected);
+
+                //resubscribe plothover
+                plot.subscribe("plothover", plothover);
+                
+                // don't fire event on the overview to prevent eternal loop
+                overview.setSelection(ranges, true);
+
+                //resubscribe choiced 
+                YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, ranges]);
+            }
+            
+            var previousPoint = null;
+
+            function plothover(o) {
+                var pos = o.pos;
+                var item = o.item;
+                
+                //YUD.get("x").innerHTML = pos.x.toFixed(2);
+                //YUD.get("y").innerHTML = pos.y.toFixed(2);
+                if (item) {
+                    if (previousPoint != item.datapoint) {
+                        previousPoint = item.datapoint;
+                        
+                        var tooltip = YUD.get("tooltip");
+                        if(tooltip) {
+                              tooltip.parentNode.removeChild(tooltip);
+                        }
+                        var x = item.datapoint.x.toFixed(2);
+                        var y = item.datapoint.y.toFixed(2);
+                        
+                        if (!item.series.label){
+                            item.series.label = 'commits';
+                        }
+                        var d = new Date(x*1000);
+                        var fd = d.toDateString()
+                        var nr_commits = parseInt(y);
+                        
+                        var cur_data = dataset[item.series.label].data[item.dataIndex];
+                        var added = cur_data.added;
+                        var changed = cur_data.changed;
+                        var removed = cur_data.removed;
+                        
+                        var nr_commits_suffix = " ${_('commits')} ";
+                        var added_suffix = " ${_('files added')} ";
+                        var changed_suffix = " ${_('files changed')} ";
+                        var removed_suffix = " ${_('files removed')} ";
+
+                        
+                        if(nr_commits == 1){nr_commits_suffix = " ${_('commit')} ";}
+                        if(added==1){added_suffix=" ${_('file added')} ";}
+                        if(changed==1){changed_suffix=" ${_('file changed')} ";}
+                        if(removed==1){removed_suffix=" ${_('file removed')} ";}
+                                                        
+                        showTooltip(item.pageX, item.pageY, item.series.label + " on " + fd
+                                 +'<br/>'+
+                                 nr_commits + nr_commits_suffix+'<br/>'+
+                                 added + added_suffix +'<br/>'+
+                                 changed + changed_suffix + '<br/>'+
+                                 removed + removed_suffix + '<br/>');
+                    }
+                }
+                else {
+                      var tooltip = YUD.get("tooltip");
+                      
+                      if(tooltip) {
+                            tooltip.parentNode.removeChild(tooltip);
+                      }
+                    previousPoint = null;
+                }
+            }
+            
+            /**
+             * MAIN EXECUTION
+             */
+            
+            var data = getDataAccordingToRanges(initial_ranges);    
+            generateCheckboxes(data);
+            
+            //main plot 
+            var plot = YAHOO.widget.Flot(plotContainer,data,plot_options);
+            
+            //overview 
+            var overview = YAHOO.widget.Flot(overviewContainer, 
+                    overview_dataset, overview_options);
+            
+            //show initial selection on overview 
+            overview.setSelection(initial_ranges);    
+            
+            plot.subscribe("plotselected", plotselected);
+            plot.subscribe("plothover", plothover)
+            
+            overview.subscribe("plotselected", function (ranges) {
+                plot.setSelection(ranges);
+            });     
+
+            YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, initial_ranges]);
+        }
+            SummaryPlot(${c.ts_min},${c.ts_max},${c.commit_data|n},${c.overview_data|n});       
+        </script>
 
 </%def>    
--- a/setup.py	Thu Oct 27 03:21:05 2011 +0200
+++ b/setup.py	Thu Oct 27 03:26:02 2011 +0200
@@ -24,7 +24,9 @@
         "python-dateutil>=1.5.0,<2.0.0",
         "dulwich>=0.8.0,<0.9.0",
         "vcs>=0.2.3.dev",
-        "webob==1.0.8"
+        "webob==1.0.8",
+        "markdown==2.0.3",
+        "docutils==0.8.1",
     ]
 
 dependency_links = [