Mercurial > kallithea
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>