##// END OF EJS Templates
implements #215 Repository view uses a README (text/markdown + rst)
marcink -
r1605:df59c050 beta
parent child Browse files
Show More
@@ -23,23 +23,25 b''
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import traceback
26 import calendar
27 import calendar
27 import logging
28 import logging
28 from time import mktime
29 from time import mktime
29 from datetime import datetime, timedelta, date
30 from datetime import timedelta, date
31 from itertools import product
30
32
31 from vcs.exceptions import ChangesetError
33 from vcs.exceptions import ChangesetError, EmptyRepositoryError, \
34 NodeDoesNotExistError
32
35
33 from pylons import tmpl_context as c, request, url
36 from pylons import tmpl_context as c, request, url
34 from pylons.i18n.translation import _
37 from pylons.i18n.translation import _
35
38
36 from rhodecode.model.db import Statistics, Repository
39 from rhodecode.model.db import Statistics
37 from rhodecode.model.repo import RepoModel
40 from rhodecode.lib import ALL_READMES, ALL_EXTS
38
39 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
41 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
40 from rhodecode.lib.base import BaseRepoController, render
42 from rhodecode.lib.base import BaseRepoController, render
41 from rhodecode.lib.utils import EmptyChangeset
43 from rhodecode.lib.utils import EmptyChangeset
42
44 from rhodecode.lib.markup_renderer import MarkupRenderer
43 from rhodecode.lib.celerylib import run_task
45 from rhodecode.lib.celerylib import run_task
44 from rhodecode.lib.celerylib.tasks import get_commits_stats, \
46 from rhodecode.lib.celerylib.tasks import get_commits_stats, \
45 LANGUAGES_EXTENSIONS_MAP
47 LANGUAGES_EXTENSIONS_MAP
@@ -48,6 +50,9 b' from rhodecode.lib.compat import json, O'
48
50
49 log = logging.getLogger(__name__)
51 log = logging.getLogger(__name__)
50
52
53 README_FILES = [''.join([x[0][0], x[1][0]]) for x in
54 sorted(list(product(ALL_READMES, ALL_EXTS)),
55 key=lambda y:y[0][1] + y[1][1])]
51
56
52 class SummaryController(BaseRepoController):
57 class SummaryController(BaseRepoController):
53
58
@@ -161,8 +166,33 b' class SummaryController(BaseRepoControll'
161 if c.enable_downloads:
166 if c.enable_downloads:
162 c.download_options = self._get_download_links(c.rhodecode_repo)
167 c.download_options = self._get_download_links(c.rhodecode_repo)
163
168
169 c.readme_data,c.readme_file = self.__get_readme_data()
164 return render('summary/summary.html')
170 return render('summary/summary.html')
165
171
172 def __get_readme_data(self):
173 readme_data = None
174 readme_file = None
175
176 try:
177 cs = c.rhodecode_repo.get_changeset('tip')
178 renderer = MarkupRenderer()
179 for f in README_FILES:
180 try:
181 readme = cs.get_node(f)
182 readme_file = f
183 readme_data = renderer.render(readme.content, f)
184 break
185 except NodeDoesNotExistError:
186 continue
187 except ChangesetError:
188 pass
189 except EmptyRepositoryError:
190 pass
191 except Exception:
192 log.error(traceback.format_exc())
193
194 return readme_data, readme_file
195
166 def _get_download_links(self, repo):
196 def _get_download_links(self, repo):
167
197
168 download_l = []
198 download_l = []
@@ -181,3 +211,4 b' class SummaryController(BaseRepoControll'
181 download_l.append(tags_group)
211 download_l.append(tags_group)
182
212
183 return download_l
213 return download_l
214
@@ -66,6 +66,34 b" ADDITIONAL_MAPPINGS = {'xaml': 'XAML'}"
66
66
67 LANGUAGES_EXTENSIONS_MAP.update(ADDITIONAL_MAPPINGS)
67 LANGUAGES_EXTENSIONS_MAP.update(ADDITIONAL_MAPPINGS)
68
68
69 # list of readme files to search in file tree and display in summary
70 # attached weights defines the search order lower is first
71 ALL_READMES = [
72 ('readme', 0), ('README', 0), ('Readme', 0),
73 ('doc/readme', 1), ('doc/README', 1), ('doc/Readme', 1),
74 ('Docs/readme', 2), ('Docs/README', 2), ('Docs/Readme', 2),
75 ('DOCS/readme', 2), ('DOCS/README', 2), ('DOCS/Readme', 2),
76 ('docs/readme', 2), ('docs/README', 2), ('docs/Readme', 2),
77 ]
78
79 # extension together with weights to search lower is first
80 RST_EXTS = [
81 ('', 0), ('.rst', 1),('.rest', 1),
82 ('.RST', 2) ,('.REST', 2),
83 ('.txt', 3), ('.TXT', 3)
84 ]
85
86 MARKDOWN_EXTS = [
87 ('.md', 1), ('.MD', 1),
88 ('.mkdn', 2), ('.MKDN', 2),
89 ('.mdown', 3), ('.MDOWN', 3),
90 ('.markdown', 4), ('.MARKDOWN', 4)
91 ]
92
93 PLAIN_EXTS = [('.text', 2),('.TEXT', 2)]
94
95 ALL_EXTS = MARKDOWN_EXTS + RST_EXTS + PLAIN_EXTS
96
69
97
70 def str2bool(_str):
98 def str2bool(_str):
71 """
99 """
@@ -3011,4 +3011,96 b' div#legend_container table td,div#legend'
3011 border: 0px solid #545454;
3011 border: 0px solid #545454;
3012 color: #AAAAAA;
3012 color: #AAAAAA;
3013 padding-left: 3px;
3013 padding-left: 3px;
3014 } No newline at end of file
3014 }
3015
3016 /*README STYLE*/
3017
3018 div.readme {
3019 padding:0px;
3020 }
3021
3022 div.readme h2 {
3023 font-weight: normal;
3024 }
3025
3026 div.readme .readme_box {
3027 background-color: #fafafa;
3028 }
3029
3030 div.readme .readme_box {
3031 clear:both;
3032 overflow:hidden;
3033 margin:0;
3034 padding:0 20px 10px;
3035 }
3036
3037 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 {
3038 border-bottom: 0 !important;
3039 margin: 0 !important;
3040 padding: 0 !important;
3041 line-height: 1.5em !important;
3042 }
3043
3044
3045 div.readme .readme_box h1:first-child {
3046 padding-top: .25em !important;
3047 }
3048
3049 div.readme .readme_box h2, div.readme .readme_box h3 {
3050 margin: 1em 0 !important;
3051 }
3052
3053 div.readme .readme_box h2 {
3054 margin-top: 1.5em !important;
3055 border-top: 4px solid #e0e0e0 !important;
3056 padding-top: .5em !important;
3057 }
3058
3059 div.readme .readme_box p {
3060 color: black !important;
3061 margin: 1em 0 !important;
3062 line-height: 1.5em !important;
3063 }
3064
3065 div.readme .readme_box ul {
3066 list-style: disc !important;
3067 margin: 1em 0 1em 2em !important;
3068 }
3069
3070 div.readme .readme_box ol {
3071 list-style: decimal;
3072 margin: 1em 0 1em 2em !important;
3073 }
3074
3075 div.readme .readme_box pre, code {
3076 font: 12px "Bitstream Vera Sans Mono","Courier",monospace;
3077 }
3078
3079 div.readme .readme_box code {
3080 font-size: 12px !important;
3081 background-color: ghostWhite !important;
3082 color: #444 !important;
3083 padding: 0 .2em !important;
3084 border: 1px solid #dedede !important;
3085 }
3086
3087 div.readme .readme_box pre code {
3088 padding: 0 !important;
3089 font-size: 12px !important;
3090 background-color: #eee !important;
3091 border: none !important;
3092 }
3093
3094 div.readme .readme_box pre {
3095 margin: 1em 0;
3096 font-size: 12px;
3097 background-color: #eee;
3098 border: 1px solid #ddd;
3099 padding: 5px;
3100 color: #444;
3101 overflow: auto;
3102 -webkit-box-shadow: rgba(0,0,0,0.07) 0 1px 2px inset;
3103 -webkit-border-radius: 3px;
3104 -moz-border-radius: 3px;
3105 border-radius: 3px;
3106 }
This diff has been collapsed as it changes many lines, (940 lines changed) Show them Hide them
@@ -64,29 +64,22 b''
64 ##FORK
64 ##FORK
65 %if c.dbrepo.fork:
65 %if c.dbrepo.fork:
66 <div style="margin-top:5px;clear:both"">
66 <div style="margin-top:5px;clear:both"">
67 <a href="${h.url('summary_home',repo_name=c.dbrepo.fork.repo_name)}">
67 <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')}"/>
68 <img class="icon" alt="${_('public')}"
68 ${_('Fork of')} ${c.dbrepo.fork.repo_name}
69 title="${_('Fork of')} ${c.dbrepo.fork.repo_name}"
70 src="${h.url('/images/icons/arrow_divide.png')}"/>
71 ${_('Fork of')} ${c.dbrepo.fork.repo_name}
72 </a>
69 </a>
73 </div>
70 </div>
74 %endif
71 %endif
75 ##REMOTE
72 ##REMOTE
76 %if c.dbrepo.clone_uri:
73 %if c.dbrepo.clone_uri:
77 <div style="margin-top:5px;clear:both">
74 <div style="margin-top:5px;clear:both">
78 <a href="${h.url(str(h.hide_credentials(c.dbrepo.clone_uri)))}">
75 <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')}"/>
79 <img class="icon" alt="${_('remote clone')}"
76 ${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}
80 title="${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}"
81 src="${h.url('/images/icons/connect.png')}"/>
82 ${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}
83 </a>
77 </a>
84 </div>
78 </div>
85 %endif
79 %endif
86 </div>
80 </div>
87 </div>
81 </div>
88
82
89
90 <div class="field">
83 <div class="field">
91 <div class="label">
84 <div class="label">
92 <label>${_('Description')}:</label>
85 <label>${_('Description')}:</label>
@@ -94,7 +87,6 b''
94 <div class="input-short desc">${h.urlify_text(c.dbrepo.description)}</div>
87 <div class="input-short desc">${h.urlify_text(c.dbrepo.description)}</div>
95 </div>
88 </div>
96
89
97
98 <div class="field">
90 <div class="field">
99 <div class="label">
91 <div class="label">
100 <label>${_('Contact')}:</label>
92 <label>${_('Contact')}:</label>
@@ -119,7 +111,6 b''
119 <span class="tooltip" title="${c.rhodecode_repo.last_change}">
111 <span class="tooltip" title="${c.rhodecode_repo.last_change}">
120 ${h.age(c.rhodecode_repo.last_change)}</span><br/>
112 ${h.age(c.rhodecode_repo.last_change)}</span><br/>
121 ${_('by')} ${h.get_changeset_safe(c.rhodecode_repo,'tip').author}
113 ${_('by')} ${h.get_changeset_safe(c.rhodecode_repo,'tip').author}
122
123 </div>
114 </div>
124 </div>
115 </div>
125
116
@@ -187,123 +178,6 b''
187 </div>
178 </div>
188 </div>
179 </div>
189 </div>
180 </div>
190 <script type="text/javascript">
191 YUE.onDOMReady(function(e){
192 id = 'clone_url';
193 YUE.on(id,'click',function(e){
194 if(YUD.hasClass(id,'selected')){
195 return
196 }
197 else{
198 YUD.addClass(id,'selected');
199 YUD.get(id).select();
200 }
201
202 })
203 })
204 var data = ${c.trending_languages|n};
205 var total = 0;
206 var no_data = true;
207 for (k in data){
208 total += data[k].count;
209 no_data = false;
210 }
211 var tbl = document.createElement('table');
212 tbl.setAttribute('class','trending_language_tbl');
213 var cnt = 0;
214 for (k in data){
215 cnt += 1;
216 var hide = cnt>2;
217 var tr = document.createElement('tr');
218 if (hide){
219 tr.setAttribute('style','display:none');
220 tr.setAttribute('class','stats_hidden');
221 }
222 var percentage = Math.round((data[k].count/total*100),2);
223 var value = data[k].count;
224 var td1 = document.createElement('td');
225 td1.width = 150;
226 var trending_language_label = document.createElement('div');
227 trending_language_label.innerHTML = data[k].desc+" ("+k+")";
228 td1.appendChild(trending_language_label);
229
230 var td2 = document.createElement('td');
231 td2.setAttribute('style','padding-right:14px !important');
232 var trending_language = document.createElement('div');
233 var nr_files = value+" ${_('files')}";
234
235 trending_language.title = k+" "+nr_files;
236
237 if (percentage>22){
238 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"% "+nr_files+ "</b>";
239 }
240 else{
241 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"%</b>";
242 }
243
244 trending_language.setAttribute("class", 'trending_language top-right-rounded-corner bottom-right-rounded-corner');
245 trending_language.style.width=percentage+"%";
246 td2.appendChild(trending_language);
247
248 tr.appendChild(td1);
249 tr.appendChild(td2);
250 tbl.appendChild(tr);
251 if(cnt == 3){
252 var show_more = document.createElement('tr');
253 var td = document.createElement('td');
254 lnk = document.createElement('a');
255
256 lnk.href='#';
257 lnk.innerHTML = "${_('show more')}";
258 lnk.id='code_stats_show_more';
259 td.appendChild(lnk);
260
261 show_more.appendChild(td);
262 show_more.appendChild(document.createElement('td'));
263 tbl.appendChild(show_more);
264 }
265
266 }
267 if(no_data){
268 var tr = document.createElement('tr');
269 var td1 = document.createElement('td');
270 td1.innerHTML = "${c.no_data_msg}";
271 tr.appendChild(td1);
272 tbl.appendChild(tr);
273 }
274 YUD.get('lang_stats').appendChild(tbl);
275 YUE.on('code_stats_show_more','click',function(){
276 l = YUD.getElementsByClassName('stats_hidden')
277 for (e in l){
278 YUD.setStyle(l[e],'display','');
279 };
280 YUD.setStyle(YUD.get('code_stats_show_more'),
281 'display','none');
282 })
283
284 var tmpl_links = {}
285 %for cnt,archive in enumerate(c.rhodecode_repo._get_archives()):
286 tmpl_links['${archive['type']}'] = '${h.link_to(archive['type'],
287 h.url('files_archive_home',repo_name=c.dbrepo.repo_name,
288 fname='__CS__'+archive['extension'],subrepos='__SUB__'),class_="archive_icon")}';
289 %endfor
290
291 YUE.on(['download_options','archive_subrepos'],'change',function(e){
292 var sm = YUD.get('download_options');
293 var new_cs = sm.options[sm.selectedIndex];
294
295 for(k in tmpl_links){
296 var s = YUD.get(k+'_link');
297 title_tmpl = "${_('Download %s as %s') % ('__CS_NAME__','__CS_EXT__')}";
298 s.title = title_tmpl.replace('__CS_NAME__',new_cs.text);
299 s.title = s.title.replace('__CS_EXT__',k);
300 var url = tmpl_links[k].replace('__CS__',new_cs.value);
301 var subrepos = YUD.get('archive_subrepos').checked
302 url = url.replace('__SUB__',subrepos);
303 s.innerHTML = url
304 }
305 });
306 </script>
307 </div>
181 </div>
308
182
309 <div class="box box-right" style="min-height:455px">
183 <div class="box box-right" style="min-height:455px">
@@ -319,7 +193,6 b''
319 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
193 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
320 ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-button-small")}
194 ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-button-small")}
321 %endif
195 %endif
322
323 %else:
196 %else:
324 ${_('Loaded in')} ${c.stats_percentage} %
197 ${_('Loaded in')} ${c.stats_percentage} %
325 %endif
198 %endif
@@ -331,333 +204,9 b''
331 <div id="legend_data" style="clear:both;margin-top:10px;">
204 <div id="legend_data" style="clear:both;margin-top:10px;">
332 <div id="legend_container"></div>
205 <div id="legend_container"></div>
333 <div id="legend_choices">
206 <div id="legend_choices">
334 <table id="legend_choices_tables" style="font-size:smaller;color:#545454"></table>
207 <table id="legend_choices_tables" class="noborder" style="font-size:smaller;color:#545454"></table>
335 </div>
208 </div>
336 </div>
209 </div>
337 <script type="text/javascript">
338 /**
339 * Plots summary graph
340 *
341 * @class SummaryPlot
342 * @param {from} initial from for detailed graph
343 * @param {to} initial to for detailed graph
344 * @param {dataset}
345 * @param {overview_dataset}
346 */
347 function SummaryPlot(from,to,dataset,overview_dataset) {
348 var initial_ranges = {
349 "xaxis":{
350 "from":from,
351 "to":to,
352 },
353 };
354 var dataset = dataset;
355 var overview_dataset = [overview_dataset];
356 var choiceContainer = YUD.get("legend_choices");
357 var choiceContainerTable = YUD.get("legend_choices_tables");
358 var plotContainer = YUD.get('commit_history');
359 var overviewContainer = YUD.get('overview');
360
361 var plot_options = {
362 bars: {show:true,align:'center',lineWidth:4},
363 legend: {show:true, container:"legend_container"},
364 points: {show:true,radius:0,fill:false},
365 yaxis: {tickDecimals:0,},
366 xaxis: {
367 mode: "time",
368 timeformat: "%d/%m",
369 min:from,
370 max:to,
371 },
372 grid: {
373 hoverable: true,
374 clickable: true,
375 autoHighlight:true,
376 color: "#999"
377 },
378 //selection: {mode: "x"}
379 };
380 var overview_options = {
381 legend:{show:false},
382 bars: {show:true,barWidth: 2,},
383 shadowSize: 0,
384 xaxis: {mode: "time", timeformat: "%d/%m/%y",},
385 yaxis: {ticks: 3, min: 0,tickDecimals:0,},
386 grid: {color: "#999",},
387 selection: {mode: "x"}
388 };
389
390 /**
391 *get dummy data needed in few places
392 */
393 function getDummyData(label){
394 return {"label":label,
395 "data":[{"time":0,
396 "commits":0,
397 "added":0,
398 "changed":0,
399 "removed":0,
400 }],
401 "schema":["commits"],
402 "color":'#ffffff',
403 }
404 }
405
406 /**
407 * generate checkboxes accordindly to data
408 * @param keys
409 * @returns
410 */
411 function generateCheckboxes(data) {
412 //append checkboxes
413 var i = 0;
414 choiceContainerTable.innerHTML = '';
415 for(var pos in data) {
416
417 data[pos].color = i;
418 i++;
419 if(data[pos].label != ''){
420 choiceContainerTable.innerHTML += '<tr><td>'+
421 '<input type="checkbox" name="' + data[pos].label +'" checked="checked" />'
422 +data[pos].label+
423 '</td></tr>';
424 }
425 }
426 }
427
428 /**
429 * ToolTip show
430 */
431 function showTooltip(x, y, contents) {
432 var div=document.getElementById('tooltip');
433 if(!div) {
434 div = document.createElement('div');
435 div.id="tooltip";
436 div.style.position="absolute";
437 div.style.border='1px solid #fdd';
438 div.style.padding='2px';
439 div.style.backgroundColor='#fee';
440 document.body.appendChild(div);
441 }
442 YUD.setStyle(div, 'opacity', 0);
443 div.innerHTML = contents;
444 div.style.top=(y + 5) + "px";
445 div.style.left=(x + 5) + "px";
446
447 var anim = new YAHOO.util.Anim(div, {opacity: {to: 0.8}}, 0.2);
448 anim.animate();
449 }
450
451 /**
452 * This function will detect if selected period has some changesets
453 for this user if it does this data is then pushed for displaying
454 Additionally it will only display users that are selected by the checkbox
455 */
456 function getDataAccordingToRanges(ranges) {
457
458 var data = [];
459 var new_dataset = {};
460 var keys = [];
461 var max_commits = 0;
462 for(var key in dataset){
463
464 for(var ds in dataset[key].data){
465 commit_data = dataset[key].data[ds];
466 if (commit_data.time >= ranges.xaxis.from && commit_data.time <= ranges.xaxis.to){
467
468 if(new_dataset[key] === undefined){
469 new_dataset[key] = {data:[],schema:["commits"],label:key};
470 }
471 new_dataset[key].data.push(commit_data);
472 }
473 }
474 if (new_dataset[key] !== undefined){
475 data.push(new_dataset[key]);
476 }
477 }
478
479 if (data.length > 0){
480 return data;
481 }
482 else{
483 //just return dummy data for graph to plot itself
484 return [getDummyData('')];
485 }
486 }
487
488 /**
489 * redraw using new checkbox data
490 */
491 function plotchoiced(e,args){
492 var cur_data = args[0];
493 var cur_ranges = args[1];
494
495 var new_data = [];
496 var inputs = choiceContainer.getElementsByTagName("input");
497
498 //show only checked labels
499 for(var i=0; i<inputs.length; i++) {
500 var checkbox_key = inputs[i].name;
501
502 if(inputs[i].checked){
503 for(var d in cur_data){
504 if(cur_data[d].label == checkbox_key){
505 new_data.push(cur_data[d]);
506 }
507 }
508 }
509 else{
510 //push dummy data to not hide the label
511 new_data.push(getDummyData(checkbox_key));
512 }
513 }
514
515 var new_options = YAHOO.lang.merge(plot_options, {
516 xaxis: {
517 min: cur_ranges.xaxis.from,
518 max: cur_ranges.xaxis.to,
519 mode:"time",
520 timeformat: "%d/%m",
521 },
522 });
523 if (!new_data){
524 new_data = [[0,1]];
525 }
526 // do the zooming
527 plot = YAHOO.widget.Flot(plotContainer, new_data, new_options);
528
529 plot.subscribe("plotselected", plotselected);
530
531 //resubscribe plothover
532 plot.subscribe("plothover", plothover);
533
534 // don't fire event on the overview to prevent eternal loop
535 overview.setSelection(cur_ranges, true);
536
537 }
538
539 /**
540 * plot only selected items from overview
541 * @param ranges
542 * @returns
543 */
544 function plotselected(ranges,cur_data) {
545 //updates the data for new plot
546 var data = getDataAccordingToRanges(ranges);
547 generateCheckboxes(data);
548
549 var new_options = YAHOO.lang.merge(plot_options, {
550 xaxis: {
551 min: ranges.xaxis.from,
552 max: ranges.xaxis.to,
553 mode:"time",
554 timeformat: "%d/%m",
555 },
556 });
557 // do the zooming
558 plot = YAHOO.widget.Flot(plotContainer, data, new_options);
559
560 plot.subscribe("plotselected", plotselected);
561
562 //resubscribe plothover
563 plot.subscribe("plothover", plothover);
564
565 // don't fire event on the overview to prevent eternal loop
566 overview.setSelection(ranges, true);
567
568 //resubscribe choiced
569 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, ranges]);
570 }
571
572 var previousPoint = null;
573
574 function plothover(o) {
575 var pos = o.pos;
576 var item = o.item;
577
578 //YUD.get("x").innerHTML = pos.x.toFixed(2);
579 //YUD.get("y").innerHTML = pos.y.toFixed(2);
580 if (item) {
581 if (previousPoint != item.datapoint) {
582 previousPoint = item.datapoint;
583
584 var tooltip = YUD.get("tooltip");
585 if(tooltip) {
586 tooltip.parentNode.removeChild(tooltip);
587 }
588 var x = item.datapoint.x.toFixed(2);
589 var y = item.datapoint.y.toFixed(2);
590
591 if (!item.series.label){
592 item.series.label = 'commits';
593 }
594 var d = new Date(x*1000);
595 var fd = d.toDateString()
596 var nr_commits = parseInt(y);
597
598 var cur_data = dataset[item.series.label].data[item.dataIndex];
599 var added = cur_data.added;
600 var changed = cur_data.changed;
601 var removed = cur_data.removed;
602
603 var nr_commits_suffix = " ${_('commits')} ";
604 var added_suffix = " ${_('files added')} ";
605 var changed_suffix = " ${_('files changed')} ";
606 var removed_suffix = " ${_('files removed')} ";
607
608
609 if(nr_commits == 1){nr_commits_suffix = " ${_('commit')} ";}
610 if(added==1){added_suffix=" ${_('file added')} ";}
611 if(changed==1){changed_suffix=" ${_('file changed')} ";}
612 if(removed==1){removed_suffix=" ${_('file removed')} ";}
613
614 showTooltip(item.pageX, item.pageY, item.series.label + " on " + fd
615 +'<br/>'+
616 nr_commits + nr_commits_suffix+'<br/>'+
617 added + added_suffix +'<br/>'+
618 changed + changed_suffix + '<br/>'+
619 removed + removed_suffix + '<br/>');
620 }
621 }
622 else {
623 var tooltip = YUD.get("tooltip");
624
625 if(tooltip) {
626 tooltip.parentNode.removeChild(tooltip);
627 }
628 previousPoint = null;
629 }
630 }
631
632 /**
633 * MAIN EXECUTION
634 */
635
636 var data = getDataAccordingToRanges(initial_ranges);
637 generateCheckboxes(data);
638
639 //main plot
640 var plot = YAHOO.widget.Flot(plotContainer,data,plot_options);
641
642 //overview
643 var overview = YAHOO.widget.Flot(overviewContainer,
644 overview_dataset, overview_options);
645
646 //show initial selection on overview
647 overview.setSelection(initial_ranges);
648
649 plot.subscribe("plotselected", plotselected);
650 plot.subscribe("plothover", plothover)
651
652 overview.subscribe("plotselected", function (ranges) {
653 plot.setSelection(ranges);
654 });
655
656 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, initial_ranges]);
657 }
658 SummaryPlot(${c.ts_min},${c.ts_max},${c.commit_data|n},${c.overview_data|n});
659 </script>
660
661 </div>
210 </div>
662 </div>
211 </div>
663
212
@@ -669,32 +218,461 b''
669 <div id="shortlog_data">
218 <div id="shortlog_data">
670 <%include file='../shortlog/shortlog_data.html'/>
219 <%include file='../shortlog/shortlog_data.html'/>
671 </div>
220 </div>
672 ##%if c.repo_changesets:
221 </div>
673 ## ${h.link_to(_('show more'),h.url('changelog_home',repo_name=c.repo_name))}
222 </div>
674 ##%endif
223
224 %if c.readme_data:
225 <div class="box" style="background-color: #FAFAFA">
226 <div class="title">
227 <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>
228 </div>
229 <div class="readme">
230 <div class="readme_box">
231 ${c.readme_data|n}
232 </div>
675 </div>
233 </div>
676 </div>
234 </div>
677 <div class="box">
235 %endif
678 <div class="title">
236
679 <div class="breadcrumbs">${h.link_to(_('Tags'),h.url('tags_home',repo_name=c.repo_name))}</div>
237 <script type="text/javascript">
680 </div>
238 YUE.onDOMReady(function(e){
681 <div class="table">
239 id = 'clone_url';
682 <%include file='../tags/tags_data.html'/>
240 YUE.on(id,'click',function(e){
683 %if c.repo_changesets:
241 if(YUD.hasClass(id,'selected')){
684 ${h.link_to(_('show more'),h.url('tags_home',repo_name=c.repo_name))}
242 return
685 %endif
243 }
686 </div>
244 else{
687 </div>
245 YUD.addClass(id,'selected');
688 <div class="box">
246 YUD.get(id).select();
689 <div class="title">
247 }
690 <div class="breadcrumbs">${h.link_to(_('Branches'),h.url('branches_home',repo_name=c.repo_name))}</div>
248
691 </div>
249 })
692 <div class="table">
250 })
693 <%include file='../branches/branches_data.html'/>
251 var data = ${c.trending_languages|n};
694 %if c.repo_changesets:
252 var total = 0;
695 ${h.link_to(_('show more'),h.url('branches_home',repo_name=c.repo_name))}
253 var no_data = true;
696 %endif
254 for (k in data){
697 </div>
255 total += data[k].count;
698 </div>
256 no_data = false;
257 }
258 var tbl = document.createElement('table');
259 tbl.setAttribute('class','trending_language_tbl');
260 var cnt = 0;
261 for (k in data){
262 cnt += 1;
263 var hide = cnt>2;
264 var tr = document.createElement('tr');
265 if (hide){
266 tr.setAttribute('style','display:none');
267 tr.setAttribute('class','stats_hidden');
268 }
269 var percentage = Math.round((data[k].count/total*100),2);
270 var value = data[k].count;
271 var td1 = document.createElement('td');
272 td1.width = 150;
273 var trending_language_label = document.createElement('div');
274 trending_language_label.innerHTML = data[k].desc+" ("+k+")";
275 td1.appendChild(trending_language_label);
276
277 var td2 = document.createElement('td');
278 td2.setAttribute('style','padding-right:14px !important');
279 var trending_language = document.createElement('div');
280 var nr_files = value+" ${_('files')}";
281
282 trending_language.title = k+" "+nr_files;
283
284 if (percentage>22){
285 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"% "+nr_files+ "</b>";
286 }
287 else{
288 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"%</b>";
289 }
290
291 trending_language.setAttribute("class", 'trending_language top-right-rounded-corner bottom-right-rounded-corner');
292 trending_language.style.width=percentage+"%";
293 td2.appendChild(trending_language);
294
295 tr.appendChild(td1);
296 tr.appendChild(td2);
297 tbl.appendChild(tr);
298 if(cnt == 3){
299 var show_more = document.createElement('tr');
300 var td = document.createElement('td');
301 lnk = document.createElement('a');
302
303 lnk.href='#';
304 lnk.innerHTML = "${_('show more')}";
305 lnk.id='code_stats_show_more';
306 td.appendChild(lnk);
307
308 show_more.appendChild(td);
309 show_more.appendChild(document.createElement('td'));
310 tbl.appendChild(show_more);
311 }
312
313 }
314 if(no_data){
315 var tr = document.createElement('tr');
316 var td1 = document.createElement('td');
317 td1.innerHTML = "${c.no_data_msg}";
318 tr.appendChild(td1);
319 tbl.appendChild(tr);
320 }
321 YUD.get('lang_stats').appendChild(tbl);
322 YUE.on('code_stats_show_more','click',function(){
323 l = YUD.getElementsByClassName('stats_hidden')
324 for (e in l){
325 YUD.setStyle(l[e],'display','');
326 };
327 YUD.setStyle(YUD.get('code_stats_show_more'),
328 'display','none');
329 })
330
331 var tmpl_links = {}
332 %for cnt,archive in enumerate(c.rhodecode_repo._get_archives()):
333 tmpl_links['${archive['type']}'] = '${h.link_to(archive['type'],
334 h.url('files_archive_home',repo_name=c.dbrepo.repo_name,
335 fname='__CS__'+archive['extension'],subrepos='__SUB__'),class_="archive_icon")}';
336 %endfor
337
338 YUE.on(['download_options','archive_subrepos'],'change',function(e){
339 var sm = YUD.get('download_options');
340 var new_cs = sm.options[sm.selectedIndex];
341
342 for(k in tmpl_links){
343 var s = YUD.get(k+'_link');
344 title_tmpl = "${_('Download %s as %s') % ('__CS_NAME__','__CS_EXT__')}";
345 s.title = title_tmpl.replace('__CS_NAME__',new_cs.text);
346 s.title = s.title.replace('__CS_EXT__',k);
347 var url = tmpl_links[k].replace('__CS__',new_cs.value);
348 var subrepos = YUD.get('archive_subrepos').checked
349 url = url.replace('__SUB__',subrepos);
350 s.innerHTML = url
351 }
352 });
353 </script>
354 <script type="text/javascript">
355 /**
356 * Plots summary graph
357 *
358 * @class SummaryPlot
359 * @param {from} initial from for detailed graph
360 * @param {to} initial to for detailed graph
361 * @param {dataset}
362 * @param {overview_dataset}
363 */
364 function SummaryPlot(from,to,dataset,overview_dataset) {
365 var initial_ranges = {
366 "xaxis":{
367 "from":from,
368 "to":to,
369 },
370 };
371 var dataset = dataset;
372 var overview_dataset = [overview_dataset];
373 var choiceContainer = YUD.get("legend_choices");
374 var choiceContainerTable = YUD.get("legend_choices_tables");
375 var plotContainer = YUD.get('commit_history');
376 var overviewContainer = YUD.get('overview');
377
378 var plot_options = {
379 bars: {show:true,align:'center',lineWidth:4},
380 legend: {show:true, container:"legend_container"},
381 points: {show:true,radius:0,fill:false},
382 yaxis: {tickDecimals:0,},
383 xaxis: {
384 mode: "time",
385 timeformat: "%d/%m",
386 min:from,
387 max:to,
388 },
389 grid: {
390 hoverable: true,
391 clickable: true,
392 autoHighlight:true,
393 color: "#999"
394 },
395 //selection: {mode: "x"}
396 };
397 var overview_options = {
398 legend:{show:false},
399 bars: {show:true,barWidth: 2,},
400 shadowSize: 0,
401 xaxis: {mode: "time", timeformat: "%d/%m/%y",},
402 yaxis: {ticks: 3, min: 0,tickDecimals:0,},
403 grid: {color: "#999",},
404 selection: {mode: "x"}
405 };
406
407 /**
408 *get dummy data needed in few places
409 */
410 function getDummyData(label){
411 return {"label":label,
412 "data":[{"time":0,
413 "commits":0,
414 "added":0,
415 "changed":0,
416 "removed":0,
417 }],
418 "schema":["commits"],
419 "color":'#ffffff',
420 }
421 }
422
423 /**
424 * generate checkboxes accordindly to data
425 * @param keys
426 * @returns
427 */
428 function generateCheckboxes(data) {
429 //append checkboxes
430 var i = 0;
431 choiceContainerTable.innerHTML = '';
432 for(var pos in data) {
433
434 data[pos].color = i;
435 i++;
436 if(data[pos].label != ''){
437 choiceContainerTable.innerHTML += '<tr><td>'+
438 '<input type="checkbox" name="' + data[pos].label +'" checked="checked" />'
439 +data[pos].label+
440 '</td></tr>';
441 }
442 }
443 }
444
445 /**
446 * ToolTip show
447 */
448 function showTooltip(x, y, contents) {
449 var div=document.getElementById('tooltip');
450 if(!div) {
451 div = document.createElement('div');
452 div.id="tooltip";
453 div.style.position="absolute";
454 div.style.border='1px solid #fdd';
455 div.style.padding='2px';
456 div.style.backgroundColor='#fee';
457 document.body.appendChild(div);
458 }
459 YUD.setStyle(div, 'opacity', 0);
460 div.innerHTML = contents;
461 div.style.top=(y + 5) + "px";
462 div.style.left=(x + 5) + "px";
463
464 var anim = new YAHOO.util.Anim(div, {opacity: {to: 0.8}}, 0.2);
465 anim.animate();
466 }
467
468 /**
469 * This function will detect if selected period has some changesets
470 for this user if it does this data is then pushed for displaying
471 Additionally it will only display users that are selected by the checkbox
472 */
473 function getDataAccordingToRanges(ranges) {
474
475 var data = [];
476 var new_dataset = {};
477 var keys = [];
478 var max_commits = 0;
479 for(var key in dataset){
480
481 for(var ds in dataset[key].data){
482 commit_data = dataset[key].data[ds];
483 if (commit_data.time >= ranges.xaxis.from && commit_data.time <= ranges.xaxis.to){
484
485 if(new_dataset[key] === undefined){
486 new_dataset[key] = {data:[],schema:["commits"],label:key};
487 }
488 new_dataset[key].data.push(commit_data);
489 }
490 }
491 if (new_dataset[key] !== undefined){
492 data.push(new_dataset[key]);
493 }
494 }
495
496 if (data.length > 0){
497 return data;
498 }
499 else{
500 //just return dummy data for graph to plot itself
501 return [getDummyData('')];
502 }
503 }
504
505 /**
506 * redraw using new checkbox data
507 */
508 function plotchoiced(e,args){
509 var cur_data = args[0];
510 var cur_ranges = args[1];
511
512 var new_data = [];
513 var inputs = choiceContainer.getElementsByTagName("input");
514
515 //show only checked labels
516 for(var i=0; i<inputs.length; i++) {
517 var checkbox_key = inputs[i].name;
518
519 if(inputs[i].checked){
520 for(var d in cur_data){
521 if(cur_data[d].label == checkbox_key){
522 new_data.push(cur_data[d]);
523 }
524 }
525 }
526 else{
527 //push dummy data to not hide the label
528 new_data.push(getDummyData(checkbox_key));
529 }
530 }
531
532 var new_options = YAHOO.lang.merge(plot_options, {
533 xaxis: {
534 min: cur_ranges.xaxis.from,
535 max: cur_ranges.xaxis.to,
536 mode:"time",
537 timeformat: "%d/%m",
538 },
539 });
540 if (!new_data){
541 new_data = [[0,1]];
542 }
543 // do the zooming
544 plot = YAHOO.widget.Flot(plotContainer, new_data, new_options);
545
546 plot.subscribe("plotselected", plotselected);
547
548 //resubscribe plothover
549 plot.subscribe("plothover", plothover);
550
551 // don't fire event on the overview to prevent eternal loop
552 overview.setSelection(cur_ranges, true);
553
554 }
555
556 /**
557 * plot only selected items from overview
558 * @param ranges
559 * @returns
560 */
561 function plotselected(ranges,cur_data) {
562 //updates the data for new plot
563 var data = getDataAccordingToRanges(ranges);
564 generateCheckboxes(data);
565
566 var new_options = YAHOO.lang.merge(plot_options, {
567 xaxis: {
568 min: ranges.xaxis.from,
569 max: ranges.xaxis.to,
570 mode:"time",
571 timeformat: "%d/%m",
572 },
573 });
574 // do the zooming
575 plot = YAHOO.widget.Flot(plotContainer, data, new_options);
576
577 plot.subscribe("plotselected", plotselected);
578
579 //resubscribe plothover
580 plot.subscribe("plothover", plothover);
581
582 // don't fire event on the overview to prevent eternal loop
583 overview.setSelection(ranges, true);
584
585 //resubscribe choiced
586 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, ranges]);
587 }
588
589 var previousPoint = null;
590
591 function plothover(o) {
592 var pos = o.pos;
593 var item = o.item;
594
595 //YUD.get("x").innerHTML = pos.x.toFixed(2);
596 //YUD.get("y").innerHTML = pos.y.toFixed(2);
597 if (item) {
598 if (previousPoint != item.datapoint) {
599 previousPoint = item.datapoint;
600
601 var tooltip = YUD.get("tooltip");
602 if(tooltip) {
603 tooltip.parentNode.removeChild(tooltip);
604 }
605 var x = item.datapoint.x.toFixed(2);
606 var y = item.datapoint.y.toFixed(2);
607
608 if (!item.series.label){
609 item.series.label = 'commits';
610 }
611 var d = new Date(x*1000);
612 var fd = d.toDateString()
613 var nr_commits = parseInt(y);
614
615 var cur_data = dataset[item.series.label].data[item.dataIndex];
616 var added = cur_data.added;
617 var changed = cur_data.changed;
618 var removed = cur_data.removed;
619
620 var nr_commits_suffix = " ${_('commits')} ";
621 var added_suffix = " ${_('files added')} ";
622 var changed_suffix = " ${_('files changed')} ";
623 var removed_suffix = " ${_('files removed')} ";
624
625
626 if(nr_commits == 1){nr_commits_suffix = " ${_('commit')} ";}
627 if(added==1){added_suffix=" ${_('file added')} ";}
628 if(changed==1){changed_suffix=" ${_('file changed')} ";}
629 if(removed==1){removed_suffix=" ${_('file removed')} ";}
630
631 showTooltip(item.pageX, item.pageY, item.series.label + " on " + fd
632 +'<br/>'+
633 nr_commits + nr_commits_suffix+'<br/>'+
634 added + added_suffix +'<br/>'+
635 changed + changed_suffix + '<br/>'+
636 removed + removed_suffix + '<br/>');
637 }
638 }
639 else {
640 var tooltip = YUD.get("tooltip");
641
642 if(tooltip) {
643 tooltip.parentNode.removeChild(tooltip);
644 }
645 previousPoint = null;
646 }
647 }
648
649 /**
650 * MAIN EXECUTION
651 */
652
653 var data = getDataAccordingToRanges(initial_ranges);
654 generateCheckboxes(data);
655
656 //main plot
657 var plot = YAHOO.widget.Flot(plotContainer,data,plot_options);
658
659 //overview
660 var overview = YAHOO.widget.Flot(overviewContainer,
661 overview_dataset, overview_options);
662
663 //show initial selection on overview
664 overview.setSelection(initial_ranges);
665
666 plot.subscribe("plotselected", plotselected);
667 plot.subscribe("plothover", plothover)
668
669 overview.subscribe("plotselected", function (ranges) {
670 plot.setSelection(ranges);
671 });
672
673 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, initial_ranges]);
674 }
675 SummaryPlot(${c.ts_min},${c.ts_max},${c.commit_data|n},${c.overview_data|n});
676 </script>
699
677
700 </%def>
678 </%def>
@@ -24,7 +24,9 b' requirements = ['
24 "python-dateutil>=1.5.0,<2.0.0",
24 "python-dateutil>=1.5.0,<2.0.0",
25 "dulwich>=0.8.0,<0.9.0",
25 "dulwich>=0.8.0,<0.9.0",
26 "vcs>=0.2.3.dev",
26 "vcs>=0.2.3.dev",
27 "webob==1.0.8"
27 "webob==1.0.8",
28 "markdown==2.0.3",
29 "docutils==0.8.1",
28 ]
30 ]
29
31
30 dependency_links = [
32 dependency_links = [
General Comments 0
You need to be logged in to leave comments. Login now