##// END OF EJS Templates
changelog: added dynamic loaders to extend number of commits inside changelog....
marcink -
r1379:191eae48 default
parent child Browse files
Show More
@@ -0,0 +1,173 b''
1 // # Copyright (C) 2016-2017 RhodeCode GmbH
2 // #
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
5 // # (only), as published by the Free Software Foundation.
6 // #
7 // # This program is distributed in the hope that it will be useful,
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // # GNU General Public License for more details.
11 // #
12 // # You should have received a copy of the GNU Affero General Public License
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 // #
15 // # This program is dual-licensed. If you wish to learn more about the
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
18
19
20 var CommitsController = function () {
21 var self = this;
22 this.$graphCanvas = $('#graph_canvas');
23 this.$commitCounter = $('#commit-counter');
24
25 this.getCurrentGraphData = function () {
26 // raw form
27 return self.$graphCanvas.data('commits');
28 };
29
30 this.setLabelText = function (graphData) {
31 var shown = $('.commit_hash').length;
32 var total = self.$commitCounter.data('total');
33
34 if (shown == 1) {
35 var text = _gettext('showing {0} out of {1} commit').format(shown, total);
36 } else {
37 var text = _gettext('showing {0} out of {1} commits').format(shown, total);
38 }
39 self.$commitCounter.html(text)
40 };
41
42 this.reloadGraph = function (chunk) {
43 chunk = chunk || 'next';
44
45 // reset state on re-render !
46 self.$graphCanvas.html('');
47
48 var edgeData = $("[data-graph]").data('graph') || this.$graphCanvas.data('graph') || [];
49
50 // Determine max number of edges per row in graph
51 var edgeCount = 1;
52 $.each(edgeData, function (i, item) {
53 $.each(item[2], function (key, value) {
54 if (value[1] > edgeCount) {
55 edgeCount = value[1];
56 }
57 });
58 });
59
60 var x_step = Math.min(10, Math.floor(86 / edgeCount));
61 var graph_options = {
62 width: 100,
63 height: $('#changesets').find('.commits-range').height(),
64 x_step: x_step,
65 y_step: 42,
66 dotRadius: 3.5,
67 lineWidth: 2.5
68 };
69
70 var prevCommitsData = this.$graphCanvas.data('commits') || [];
71 var nextCommitsData = $("[data-graph]").data('commits') || [];
72
73 if (chunk == 'next') {
74 var commitData = $.merge(prevCommitsData, nextCommitsData);
75 } else {
76 var commitData = $.merge(nextCommitsData, prevCommitsData);
77 }
78
79 this.$graphCanvas.data('graph', edgeData);
80 this.$graphCanvas.data('commits', commitData);
81
82 // destroy dynamic loaded graph
83 $("[data-graph]").remove();
84
85 this.$graphCanvas.commits(graph_options);
86
87 this.setLabelText(edgeData);
88 if ($('.load-more-commits').find('.prev-commits').get(0)) {
89 var padding = 75;
90
91 } else {
92 var padding = 43;
93 }
94 $('#graph_nodes').css({'padding-top': padding});
95 };
96
97 this.getChunkUrl = function (page, chunk, branch) {
98 var urlData = {
99 'repo_name': templateContext.repo_name,
100 'page': page,
101 'chunk': chunk
102 };
103
104 if (branch !== undefined && branch !== '') {
105 urlData['branch'] = branch;
106 }
107
108 return pyroutes.url('changelog_elements', urlData);
109 };
110
111 this.loadNext = function (node, page, branch) {
112 var loadUrl = this.getChunkUrl(page, 'next', branch);
113 var postData = {'graph': JSON.stringify(this.getCurrentGraphData())};
114
115 $.post(loadUrl, postData, function (data) {
116 $(node).closest('tbody').append(data);
117 $(node).closest('td').remove();
118 self.reloadGraph('next');
119 })
120 };
121
122 this.loadPrev = function (node, page, branch) {
123 var loadUrl = this.getChunkUrl(page, 'prev', branch);
124 var postData = {'graph': JSON.stringify(this.getCurrentGraphData())};
125
126 $.post(loadUrl, postData, function (data) {
127 $(node).closest('tbody').prepend(data);
128 $(node).closest('td').remove();
129 self.reloadGraph('prev');
130 })
131 };
132
133 this.expandCommit = function (node) {
134
135 var target_expand = $(node);
136 var cid = target_expand.data('commitId');
137
138 if (target_expand.hasClass('open')) {
139 $('#c-' + cid).css({
140 'height': '1.5em',
141 'white-space': 'nowrap',
142 'text-overflow': 'ellipsis',
143 'overflow': 'hidden'
144 });
145 $('#t-' + cid).css({
146 'height': 'auto',
147 'line-height': '.9em',
148 'text-overflow': 'ellipsis',
149 'overflow': 'hidden',
150 'white-space': 'nowrap'
151 });
152 target_expand.removeClass('open');
153 }
154 else {
155 $('#c-' + cid).css({
156 'height': 'auto',
157 'white-space': 'pre-line',
158 'text-overflow': 'initial',
159 'overflow': 'visible'
160 });
161 $('#t-' + cid).css({
162 'height': 'auto',
163 'max-height': 'none',
164 'text-overflow': 'initial',
165 'overflow': 'visible',
166 'white-space': 'normal'
167 });
168 target_expand.addClass('open');
169 }
170 // redraw the graph
171 self.reloadGraph();
172 }
173 };
@@ -68,6 +68,7 b''
68 "<%= dirs.js.src %>/rhodecode/utils/os.js",
68 "<%= dirs.js.src %>/rhodecode/utils/os.js",
69 "<%= dirs.js.src %>/rhodecode/utils/topics.js",
69 "<%= dirs.js.src %>/rhodecode/utils/topics.js",
70 "<%= dirs.js.src %>/rhodecode/init.js",
70 "<%= dirs.js.src %>/rhodecode/init.js",
71 "<%= dirs.js.src %>/rhodecode/changelog.js",
71 "<%= dirs.js.src %>/rhodecode/codemirror.js",
72 "<%= dirs.js.src %>/rhodecode/codemirror.js",
72 "<%= dirs.js.src %>/rhodecode/comments.js",
73 "<%= dirs.js.src %>/rhodecode/comments.js",
73 "<%= dirs.js.src %>/rhodecode/constants.js",
74 "<%= dirs.js.src %>/rhodecode/constants.js",
@@ -997,10 +997,10 b' def make_map(config):'
997 conditions={'function': check_repo},
997 conditions={'function': check_repo},
998 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
998 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
999
999
1000 rmap.connect('changelog_details', '/{repo_name}/changelog_details/{cs}',
1000 rmap.connect('changelog_elements', '/{repo_name}/changelog_details',
1001 controller='changelog', action='changelog_details',
1001 controller='changelog', action='changelog_elements',
1002 conditions={'function': check_repo},
1002 conditions={'function': check_repo},
1003 requirements=URL_NAME_REQUIREMENTS)
1003 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1004
1004
1005 rmap.connect('files_home', '/{repo_name}/files/{revision}/{f_path}',
1005 rmap.connect('files_home', '/{repo_name}/files/{revision}/{f_path}',
1006 controller='files', revision='tip', f_path='',
1006 controller='files', revision='tip', f_path='',
@@ -30,7 +30,8 b' from pylons.i18n.translation import _'
30 from webob.exc import HTTPNotFound, HTTPBadRequest
30 from webob.exc import HTTPNotFound, HTTPBadRequest
31
31
32 import rhodecode.lib.helpers as h
32 import rhodecode.lib.helpers as h
33 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
33 from rhodecode.lib.auth import (
34 LoginRequired, HasRepoPermissionAnyDecorator, XHRRequired)
34 from rhodecode.lib.base import BaseRepoController, render
35 from rhodecode.lib.base import BaseRepoController, render
35 from rhodecode.lib.ext_json import json
36 from rhodecode.lib.ext_json import json
36 from rhodecode.lib.graphmod import _colored, _dagwalker
37 from rhodecode.lib.graphmod import _colored, _dagwalker
@@ -98,7 +99,7 b' class ChangelogController(BaseRepoContro'
98 redirect(h.url('changelog_home', repo_name=repo.repo_name))
99 redirect(h.url('changelog_home', repo_name=repo.repo_name))
99 raise HTTPBadRequest()
100 raise HTTPBadRequest()
100
101
101 def _graph(self, repo, commits):
102 def _graph(self, repo, commits, prev_data=None, next_data=None):
102 """
103 """
103 Generates a DAG graph for repo
104 Generates a DAG graph for repo
104
105
@@ -106,12 +107,30 b' class ChangelogController(BaseRepoContro'
106 :param commits: list of commits
107 :param commits: list of commits
107 """
108 """
108 if not commits:
109 if not commits:
109 c.jsdata = json.dumps([])
110 return json.dumps([])
110 return
111
112 def serialize(commit, parents=True):
113 data = dict(
114 raw_id=commit.raw_id,
115 idx=commit.idx,
116 branch=commit.branch,
117 )
118 if parents:
119 data['parents'] = [
120 serialize(x, parents=False) for x in commit.parents]
121 return data
122
123 prev_data = prev_data or []
124 next_data = next_data or []
125
126 current = [serialize(x) for x in commits]
127 commits = prev_data + current + next_data
111
128
112 dag = _dagwalker(repo, commits)
129 dag = _dagwalker(repo, commits)
113 data = [['', vtx, edges] for vtx, edges in _colored(dag)]
130
114 c.jsdata = json.dumps(data)
131 data = [[commit_id, vtx, edges, branch]
132 for commit_id, vtx, edges, branch in _colored(dag)]
133 return json.dumps(data), json.dumps(current)
115
134
116 def _check_if_valid_branch(self, branch_name, repo_name, f_path):
135 def _check_if_valid_branch(self, branch_name, repo_name, f_path):
117 if branch_name not in c.rhodecode_repo.branches_all:
136 if branch_name not in c.rhodecode_repo.branches_all:
@@ -120,26 +139,37 b' class ChangelogController(BaseRepoContro'
120 redirect(url('changelog_file_home', repo_name=repo_name,
139 redirect(url('changelog_file_home', repo_name=repo_name,
121 revision=branch_name, f_path=f_path or ''))
140 revision=branch_name, f_path=f_path or ''))
122
141
142 def _load_changelog_data(self, collection, page, chunk_size, branch_name=None, dynamic=False):
143 c.total_cs = len(collection)
144 c.showing_commits = min(chunk_size, c.total_cs)
145 c.pagination = RepoPage(collection, page=page, item_count=c.total_cs,
146 items_per_page=chunk_size, branch=branch_name)
147
148 c.next_page = c.pagination.next_page
149 c.prev_page = c.pagination.previous_page
150
151 if dynamic:
152 if request.GET.get('chunk') != 'next':
153 c.next_page = None
154 if request.GET.get('chunk') != 'prev':
155 c.prev_page = None
156
157 page_commit_ids = [x.raw_id for x in c.pagination]
158 c.comments = c.rhodecode_db_repo.get_comments(page_commit_ids)
159 c.statuses = c.rhodecode_db_repo.statuses(page_commit_ids)
160
123 @LoginRequired()
161 @LoginRequired()
124 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
162 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
125 'repository.admin')
163 'repository.admin')
126 def index(self, repo_name, revision=None, f_path=None):
164 def index(self, repo_name, revision=None, f_path=None):
127 commit_id = revision
165 commit_id = revision
128 limit = 100
166 chunk_size = 20
167
168 c.branch_name = branch_name = request.GET.get('branch', None)
169 c.book_name = book_name = request.GET.get('bookmark', None)
129 hist_limit = safe_int(request.GET.get('limit')) or None
170 hist_limit = safe_int(request.GET.get('limit')) or None
130 if request.GET.get('size'):
131 c.size = safe_int(request.GET.get('size'), 1)
132 session['changelog_size'] = c.size
133 session.save()
134 else:
135 c.size = int(session.get('changelog_size', DEFAULT_CHANGELOG_SIZE))
136
137 # min size must be 1 and less than limit
138 c.size = max(c.size, 1) if c.size <= limit else limit
139
171
140 p = safe_int(request.GET.get('page', 1), 1)
172 p = safe_int(request.GET.get('page', 1), 1)
141 c.branch_name = branch_name = request.GET.get('branch', None)
142 c.book_name = book_name = request.GET.get('bookmark', None)
143
173
144 c.selected_name = branch_name or book_name
174 c.selected_name = branch_name or book_name
145 if not commit_id and branch_name:
175 if not commit_id and branch_name:
@@ -147,6 +177,8 b' class ChangelogController(BaseRepoContro'
147
177
148 c.changelog_for_path = f_path
178 c.changelog_for_path = f_path
149 pre_load = ['author', 'branch', 'date', 'message', 'parents']
179 pre_load = ['author', 'branch', 'date', 'message', 'parents']
180 commit_ids = []
181
150 try:
182 try:
151 if f_path:
183 if f_path:
152 log.debug('generating changelog for path %s', f_path)
184 log.debug('generating changelog for path %s', f_path)
@@ -174,13 +206,9 b' class ChangelogController(BaseRepoContro'
174 collection = c.rhodecode_repo.get_commits(
206 collection = c.rhodecode_repo.get_commits(
175 branch_name=branch_name, pre_load=pre_load)
207 branch_name=branch_name, pre_load=pre_load)
176
208
177 c.total_cs = len(collection)
209 self._load_changelog_data(
178 c.showing_commits = min(c.size, c.total_cs)
210 collection, p, chunk_size, c.branch_name, dynamic=f_path)
179 c.pagination = RepoPage(collection, page=p, item_count=c.total_cs,
211
180 items_per_page=c.size, branch=branch_name)
181 page_commit_ids = [x.raw_id for x in c.pagination]
182 c.comments = c.rhodecode_db_repo.get_comments(page_commit_ids)
183 c.statuses = c.rhodecode_db_repo.statuses(page_commit_ids)
184 except EmptyRepositoryError as e:
212 except EmptyRepositoryError as e:
185 h.flash(safe_str(e), category='warning')
213 h.flash(safe_str(e), category='warning')
186 return redirect(url('summary_home', repo_name=repo_name))
214 return redirect(url('summary_home', repo_name=repo_name))
@@ -195,22 +223,62 b' class ChangelogController(BaseRepoContro'
195 # loading from ajax, we don't want the first result, it's popped
223 # loading from ajax, we don't want the first result, it's popped
196 return render('changelog/changelog_file_history.mako')
224 return render('changelog/changelog_file_history.mako')
197
225
198 if f_path:
226 if not f_path:
199 revs = []
227 commit_ids = c.pagination
200 else:
228
201 revs = c.pagination
229 c.graph_data, c.graph_commits = self._graph(
202 self._graph(c.rhodecode_repo, revs)
230 c.rhodecode_repo, commit_ids)
203
231
204 return render('changelog/changelog.mako')
232 return render('changelog/changelog.mako')
205
233
206 @LoginRequired()
234 @LoginRequired()
207 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
235 @XHRRequired()
208 'repository.admin')
236 @HasRepoPermissionAnyDecorator(
209 def changelog_details(self, commit_id):
237 'repository.read', 'repository.write', 'repository.admin')
210 if request.environ.get('HTTP_X_PARTIAL_XHR'):
238 def changelog_elements(self, repo_name):
211 c.commit = c.rhodecode_repo.get_commit(commit_id=commit_id)
239 commit_id = None
212 return render('changelog/changelog_details.mako')
240 chunk_size = 20
213 raise HTTPNotFound()
241
242 def wrap_for_error(err):
243 return '<tr><td colspan="9" class="alert alert-error">ERROR: {}</td></tr>'.format(err)
244
245 c.branch_name = branch_name = request.GET.get('branch', None)
246 c.book_name = book_name = request.GET.get('bookmark', None)
247
248 p = safe_int(request.GET.get('page', 1), 1)
249
250 c.selected_name = branch_name or book_name
251 if not commit_id and branch_name:
252 if branch_name not in c.rhodecode_repo.branches_all:
253 return wrap_for_error(
254 safe_str('Missing branch: {}'.format(branch_name)))
255
256 pre_load = ['author', 'branch', 'date', 'message', 'parents']
257 collection = c.rhodecode_repo.get_commits(
258 branch_name=branch_name, pre_load=pre_load)
259
260 try:
261 self._load_changelog_data(collection, p, chunk_size, dynamic=True)
262 except EmptyRepositoryError as e:
263 return wrap_for_error(safe_str(e))
264 except (RepositoryError, CommitDoesNotExistError, Exception) as e:
265 log.exception('Failed to fetch commits')
266 return wrap_for_error(safe_str(e))
267
268 prev_data = None
269 next_data = None
270
271 prev_graph = json.loads(request.POST.get('graph', ''))
272
273 if request.GET.get('chunk') == 'prev':
274 next_data = prev_graph
275 elif request.GET.get('chunk') == 'next':
276 prev_data = prev_graph
277
278 c.graph_data, c.graph_commits = self._graph(
279 c.rhodecode_repo, c.pagination,
280 prev_data=prev_data, next_data=next_data)
281 return render('changelog/changelog_elements.mako')
214
282
215 @LoginRequired()
283 @LoginRequired()
216 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
284 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
@@ -21,7 +21,7 b''
21 """
21 """
22 Modified mercurial DAG graph functions that re-uses VCS structure
22 Modified mercurial DAG graph functions that re-uses VCS structure
23
23
24 It allows to have a shared codebase for DAG generation for hg and git repos
24 It allows to have a shared codebase for DAG generation for hg, git, svn repos
25 """
25 """
26
26
27 nullrev = -1
27 nullrev = -1
@@ -51,36 +51,30 b' def _dagwalker(repo, commits):'
51 if not commits:
51 if not commits:
52 return
52 return
53
53
54 # TODO: johbo: Use some sort of vcs api here
54 def get_parent_indexes(idx):
55 if repo.alias == 'hg':
55 return [commit.idx for commit in repo[idx].parents]
56 def get_parent_indexes(idx):
57 return repo._remote.ctx_parents(idx)
58
56
59 elif repo.alias in ['git', 'svn']:
57 indexes = [commit['idx'] for commit in commits]
60 def get_parent_indexes(idx):
61 return [commit.idx for commit in repo[idx].parents]
62
63 indexes = [commit.idx for commit in commits]
64 lowest_idx = min(indexes)
58 lowest_idx = min(indexes)
65 known_indexes = set(indexes)
59 known_indexes = set(indexes)
66
60
67 gpcache = {}
61 grandparnet_cache = {}
68 for commit in commits:
62 for commit in commits:
69 parents = sorted(set([p.idx for p in commit.parents
63 parents = sorted(set([p['idx'] for p in commit['parents']
70 if p.idx in known_indexes]))
64 if p['idx'] in known_indexes]))
71 mpars = [p.idx for p in commit.parents if
65 mpars = [p['idx'] for p in commit['parents'] if
72 p.idx != nullrev and p.idx not in parents]
66 p['idx'] != nullrev and p['idx'] not in parents]
73 for mpar in mpars:
67 for mpar in mpars:
74 gp = gpcache.get(mpar)
68 gp = grandparnet_cache.get(mpar)
75 if gp is None:
69 if gp is None:
76 gp = gpcache[mpar] = grandparent(
70 gp = grandparnet_cache[mpar] = grandparent(
77 get_parent_indexes, lowest_idx, indexes, mpar)
71 get_parent_indexes, lowest_idx, indexes, mpar)
78 if not gp:
72 if not gp:
79 parents.append(mpar)
73 parents.append(mpar)
80 else:
74 else:
81 parents.extend(g for g in gp if g not in parents)
75 parents.extend(g for g in gp if g not in parents)
82
76
83 yield (commit.idx, parents)
77 yield (commit['raw_id'], commit['idx'], parents, commit['branch'])
84
78
85
79
86 def _colored(dag):
80 def _colored(dag):
@@ -100,7 +94,7 b' def _colored(dag):'
100 colors = {}
94 colors = {}
101 newcolor = 1
95 newcolor = 1
102
96
103 for commit_idx, parents in dag:
97 for commit_id, commit_idx, parents, branch in dag:
104
98
105 # Compute seen and next_
99 # Compute seen and next_
106 if commit_idx not in seen:
100 if commit_idx not in seen:
@@ -137,7 +131,7 b' def _colored(dag):'
137 for p in parents])
131 for p in parents])
138
132
139 # Yield and move on
133 # Yield and move on
140 yield ((col, color), edges)
134 yield (commit_id, (col, color), edges, branch)
141 seen = next_
135 seen = next_
142
136
143
137
@@ -957,6 +957,18 b' label {'
957 clear: left;
957 clear: left;
958 }
958 }
959 }
959 }
960
961 .load-more-commits {
962 text-align: center;
963 }
964 .load-more-commits:hover {
965 background-color: @grey7;
966 }
967 .load-more-commits {
968 a {
969 display: block;
970 }
971 }
960 }
972 }
961
973
962 #filter_changelog {
974 #filter_changelog {
@@ -50,7 +50,8 b' h6, .h6 { font-size: 1em; font-family'
50
50
51 .breadcrumbs_light {
51 .breadcrumbs_light {
52 float:left;
52 float:left;
53 margin: @padding 0;
53 font-size: 1.3em;
54 line-height: 38px;
54 }
55 }
55
56
56 // Body text
57 // Body text
@@ -21,7 +21,7 b' function Route( commit, data, options ) '
21 self.branch = data[2];
21 self.branch = data[2];
22 }
22 }
23
23
24 Route.prototype.drawRoute = function ( ctx ) {
24 Route.prototype.drawRoute = function ( commit, ctx ) {
25 var self = this;
25 var self = this;
26
26
27 if (self.options.orientation === "horizontal") {
27 if (self.options.orientation === "horizontal") {
@@ -52,12 +52,13 b' Route.prototype.drawRoute = function ( c'
52
52
53 } else {
53 } else {
54 var from_x = self.options.width * self.options.scaleFactor - (self.from + 1) * self.options.x_step * self.options.scaleFactor;
54 var from_x = self.options.width * self.options.scaleFactor - (self.from + 1) * self.options.x_step * self.options.scaleFactor;
55 var row = $("#chg_"+(self.commit.idx+1))
55 var row = $("#sha_" + commit.sha);
56 if (row.length) {
56 if (row.length) {
57 var from_y = (row.offset().top + row.height() / 2 - self.options.relaOffset) * self.options.scaleFactor;
57 var from_y = (row.offset().top + row.height() / 2 - self.options.relaOffset) * self.options.scaleFactor;
58 }
58 }
59 var to_x = self.options.width * self.options.scaleFactor - (self.to + 1) * self.options.x_step * self.options.scaleFactor;
59 var to_x = self.options.width * self.options.scaleFactor - (self.to + 1) * self.options.x_step * self.options.scaleFactor;
60 var next_row = $("#chg_"+(self.commit.idx+2))
60 var next_row = $("#sha_" + commit.sha).next('tr');
61
61 if (next_row.length) {
62 if (next_row.length) {
62 var to_y = ((next_row.offset().top + next_row.height() / 2 - self.options.relaOffset) + 0.2) * self.options.scaleFactor;
63 var to_y = ((next_row.offset().top + next_row.height() / 2 - self.options.relaOffset) + 0.2) * self.options.scaleFactor;
63 }
64 }
@@ -108,7 +109,8 b' Commit.prototype.drawDot = function ( ct'
108
109
109 } else {
110 } else {
110 var x = self.options.width * self.options.scaleFactor - (self.dot_offset + 1) * self.options.x_step * self.options.scaleFactor;
111 var x = self.options.width * self.options.scaleFactor - (self.dot_offset + 1) * self.options.x_step * self.options.scaleFactor;
111 var row = $("#chg_"+(self.idx+1))
112 var row = $("#sha_" + self.sha);
113
112 var y = (row.offset().top + row.height() / 2 - self.options.relaOffset) * self.options.scaleFactor;
114 var y = (row.offset().top + row.height() / 2 - self.options.relaOffset) * self.options.scaleFactor;
113 ctx.fillStyle = self.graph.get_color(self.dot_branch);
115 ctx.fillStyle = self.graph.get_color(self.dot_branch);
114 ctx.beginPath();
116 ctx.beginPath();
@@ -218,7 +220,7 b' GraphCanvas.prototype.draw = function ()'
218
220
219 ctx.lineWidth = self.options.lineWidth;
221 ctx.lineWidth = self.options.lineWidth;
220
222
221 self.options.relaOffset = $("#chg_1").offset().top;
223 self.options.relaOffset = $(".changelogRow").first().offset().top;
222
224
223 var n_commits = self.data.length;
225 var n_commits = self.data.length;
224 for (var i=0; i<n_commits; i++) {
226 for (var i=0; i<n_commits; i++) {
@@ -226,7 +228,7 b' GraphCanvas.prototype.draw = function ()'
226
228
227 for (var j=0; j<commit.routes.length; j++) {
229 for (var j=0; j<commit.routes.length; j++) {
228 var route = commit.routes[j];
230 var route = commit.routes[j];
229 route.drawRoute(ctx);
231 route.drawRoute(commit, ctx);
230 }
232 }
231 commit.drawDot(ctx);
233 commit.drawDot(ctx);
232 }
234 }
@@ -44,6 +44,7 b' function registerRCRoutes() {'
44 pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request-comment/%(comment_id)s/delete', ['repo_name', 'comment_id']);
44 pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request-comment/%(comment_id)s/delete', ['repo_name', 'comment_id']);
45 pyroutes.register('changelog_home', '/%(repo_name)s/changelog', ['repo_name']);
45 pyroutes.register('changelog_home', '/%(repo_name)s/changelog', ['repo_name']);
46 pyroutes.register('changelog_file_home', '/%(repo_name)s/changelog/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
46 pyroutes.register('changelog_file_home', '/%(repo_name)s/changelog/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
47 pyroutes.register('changelog_elements', '/%(repo_name)s/changelog_details', ['repo_name']);
47 pyroutes.register('files_home', '/%(repo_name)s/files/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
48 pyroutes.register('files_home', '/%(repo_name)s/files/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
48 pyroutes.register('files_history_home', '/%(repo_name)s/history/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
49 pyroutes.register('files_history_home', '/%(repo_name)s/history/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
49 pyroutes.register('files_authors_home', '/%(repo_name)s/authors/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
50 pyroutes.register('files_authors_home', '/%(repo_name)s/authors/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
@@ -16,7 +16,6 b''
16 %if c.changelog_for_path:
16 %if c.changelog_for_path:
17 /${c.changelog_for_path}
17 /${c.changelog_for_path}
18 %endif
18 %endif
19 ${ungettext('showing %d out of %d commit', 'showing %d out of %d commits', c.showing_commits) % (c.showing_commits, c.total_cs)}
20 </%def>
19 </%def>
21
20
22 <%def name="menu_bar_nav()">
21 <%def name="menu_bar_nav()">
@@ -74,6 +73,7 b''
74 </div>
73 </div>
75
74
76 % if c.pagination:
75 % if c.pagination:
76 <script type="text/javascript" src="${h.asset('js/jquery.commits-graph.js')}"></script>
77
77
78 <div class="graph-header">
78 <div class="graph-header">
79 <div id="filter_changelog">
79 <div id="filter_changelog">
@@ -85,134 +85,53 b''
85 %endif
85 %endif
86 </div>
86 </div>
87 ${self.breadcrumbs('breadcrumbs_light')}
87 ${self.breadcrumbs('breadcrumbs_light')}
88 <div id="commit-counter" data-total=${c.total_cs} class="pull-right">
89 ${ungettext('showing %d out of %d commit', 'showing %d out of %d commits', c.showing_commits) % (c.showing_commits, c.total_cs)}
90 </div>
88 </div>
91 </div>
89
92
90 <div id="graph">
93 <div id="graph">
91 <div class="graph-col-wrapper">
94 <div class="graph-col-wrapper">
92 <div id="graph_nodes">
95 <div id="graph_nodes">
93 <div id="graph_canvas" data-graph='${c.jsdata|n}'></div>
96 <div id="graph_canvas"></div>
94 </div>
97 </div>
95 <div id="graph_content" class="main-content graph_full_width">
98 <div id="graph_content" class="main-content graph_full_width">
96
97 <div class="table">
98 <table id="changesets" class="rctable">
99 <tr>
100 ## checkbox
101 <th></th>
102 <th colspan="2"></th>
103
99
104 <th>${_('Commit')}</th>
100 <div class="table">
105 ## commit message expand arrow
101 <table id="changesets" class="rctable">
106 <th></th>
102 <tr>
107 <th>${_('Commit Message')}</th>
103 ## checkbox
108
104 <th></th>
109 <th>${_('Age')}</th>
105 <th colspan="2"></th>
110 <th>${_('Author')}</th>
111
106
112 <th>${_('Refs')}</th>
107 <th>${_('Commit')}</th>
113 </tr>
108 ## commit message expand arrow
114 <tbody>
109 <th></th>
115 %for cnt,commit in enumerate(c.pagination):
110 <th>${_('Commit Message')}</th>
116 <tr id="chg_${cnt+1}" class="container ${'tablerow%s' % (cnt%2)}">
117
118 <td class="td-checkbox">
119 ${h.checkbox(commit.raw_id,class_="commit-range")}
120 </td>
121 <td class="td-status">
122
111
123 %if c.statuses.get(commit.raw_id):
112 <th>${_('Age')}</th>
124 <div class="changeset-status-ico">
113 <th>${_('Author')}</th>
125 %if c.statuses.get(commit.raw_id)[2]:
114
126 <a class="tooltip" title="${_('Commit status: %s\nClick to open associated pull request #%s') % (h.commit_status_lbl(c.statuses.get(commit.raw_id)[0]), c.statuses.get(commit.raw_id)[2])}" href="${h.url('pullrequest_show',repo_name=c.statuses.get(commit.raw_id)[3],pull_request_id=c.statuses.get(commit.raw_id)[2])}">
115 <th>${_('Refs')}</th>
127 <div class="${'flag_status %s' % c.statuses.get(commit.raw_id)[0]}"></div>
116 </tr>
128 </a>
129 %else:
130 <a class="tooltip" title="${_('Commit status: %s') % h.commit_status_lbl(c.statuses.get(commit.raw_id)[0])}" href="${h.url('changeset_home',repo_name=c.repo_name,revision=commit.raw_id,anchor='comment-%s' % c.comments[commit.raw_id][0].comment_id)}">
131 <div class="${'flag_status %s' % c.statuses.get(commit.raw_id)[0]}"></div>
132 </a>
133 %endif
134 </div>
135 %else:
136 <div class="tooltip flag_status not_reviewed" title="${_('Commit status: Not Reviewed')}"></div>
137 %endif
138 </td>
139 <td class="td-comments comments-col">
140 %if c.comments.get(commit.raw_id):
141 <a title="${_('Commit has comments')}" href="${h.url('changeset_home',repo_name=c.repo_name,revision=commit.raw_id,anchor='comment-%s' % c.comments[commit.raw_id][0].comment_id)}">
142 <i class="icon-comment"></i> ${len(c.comments[commit.raw_id])}
143 </a>
144 %endif
145 </td>
146 <td class="td-hash">
147 <code>
148 <a href="${h.url('changeset_home',repo_name=c.repo_name,revision=commit.raw_id)}">
149 <span class="commit_hash">${h.show_id(commit)}</span>
150 </a>
151 </code>
152 </td>
153 <td class="td-message expand_commit" data-commit-id="${commit.raw_id}" title="${_('Expand commit message')}">
154 <div class="show_more_col">
155 <i class="show_more"></i>&nbsp;
156 </div>
157 </td>
158 <td class="td-description mid">
159 <div class="log-container truncate-wrap">
160 <div class="message truncate" id="c-${commit.raw_id}">${h.urlify_commit_message(commit.message, c.repo_name)}</div>
161 </div>
162 </td>
163
117
164 <td class="td-time">
118 <tbody class="commits-range">
165 ${h.age_component(commit.date)}
119 <%include file='changelog_elements.mako'/>
166 </td>
120 </tbody>
167 <td class="td-user">
121 </table>
168 ${self.gravatar_with_user(commit.author)}
122 </div>
169 </td>
123 </div>
170
124 <div class="pagination-wh pagination-left">
171 <td class="td-tags tags-col">
125 ${c.pagination.pager('$link_previous ~2~ $link_next')}
172 <div id="t-${commit.raw_id}">
126 </div>
173 ## branch
127 </div>
174 %if commit.branch:
175 <span class="branchtag tag" title="${_('Branch %s') % commit.branch}">
176 <a href="${h.url('changelog_home',repo_name=c.repo_name,branch=commit.branch)}"><i class="icon-code-fork"></i>${h.shorter(commit.branch)}</a>
177 </span>
178 %endif
179
128
180 ## bookmarks
129 <script type="text/javascript">
181 %if h.is_hg(c.rhodecode_repo):
182 %for book in commit.bookmarks:
183 <span class="tag booktag" title="${_('Bookmark %s') % book}">
184 <a href="${h.url('files_home',repo_name=c.repo_name,revision=commit.raw_id)}"><i class="icon-bookmark"></i>${h.shorter(book)}</a>
185 </span>
186 %endfor
187 %endif
188
189 ## tags
190 %for tag in commit.tags:
191 <span class="tagtag tag" title="${_('Tag %s') % tag}">
192 <a href="${h.url('files_home',repo_name=c.repo_name,revision=commit.raw_id)}"><i class="icon-tag"></i>${h.shorter(tag)}</a>
193 </span>
194 %endfor
195
196 </div>
197 </td>
198 </tr>
199 %endfor
200 </tbody>
201 </table>
202 </div>
203 </div>
204 </div>
205 <div class="pagination-wh pagination-left">
206 ${c.pagination.pager('$link_previous ~2~ $link_next')}
207 </div>
208
209 <script type="text/javascript" src="${h.asset('js/jquery.commits-graph.js')}"></script>
210 <script type="text/javascript">
211 var cache = {};
130 var cache = {};
212 $(function(){
131 $(function(){
213
132
214 // Create links to commit ranges when range checkboxes are selected
133 // Create links to commit ranges when range checkboxes are selected
215 var $commitCheckboxes = $('.commit-range');
134 var $commitCheckboxes = $('.commit-range');
216 // cache elements
135 // cache elements
217 var $commitRangeContainer = $('#rev_range_container');
136 var $commitRangeContainer = $('#rev_range_container');
218 var $commitRangeClear = $('#rev_range_clear');
137 var $commitRangeClear = $('#rev_range_clear');
@@ -293,7 +212,6 b''
293 // make sure the buttons are consistent when navigate back and forth
212 // make sure the buttons are consistent when navigate back and forth
294 checkboxRangeSelector();
213 checkboxRangeSelector();
295
214
296
297 var msgs = $('.message');
215 var msgs = $('.message');
298 // get first element height
216 // get first element height
299 var el = $('#graph_content .container')[0];
217 var el = $('#graph_content .container')[0];
@@ -310,48 +228,6 b''
310 }
228 }
311 }
229 }
312
230
313 $('.expand_commit').on('click', function (e) {
314 var target_expand = $(this);
315 var cid = target_expand.data('commitId');
316
317 if (target_expand.hasClass('open')) {
318 $('#c-' + cid).css({
319 'height': '1.5em',
320 'white-space': 'nowrap',
321 'text-overflow': 'ellipsis',
322 'overflow': 'hidden'
323 });
324 $('#t-' + cid).css({
325 'height': 'auto',
326 'line-height': '.9em',
327 'text-overflow': 'ellipsis',
328 'overflow': 'hidden',
329 'white-space': 'nowrap'
330 });
331 target_expand.removeClass('open');
332 }
333 else {
334 $('#c-' + cid).css({
335 'height': 'auto',
336 'white-space': 'pre-line',
337 'text-overflow': 'initial',
338 'overflow': 'visible'
339 });
340 $('#t-' + cid).css({
341 'height': 'auto',
342 'max-height': 'none',
343 'text-overflow': 'initial',
344 'overflow': 'visible',
345 'white-space': 'normal'
346 });
347 target_expand.addClass('open');
348 }
349 // redraw the graph
350 graph_options.height = $("#changesets").height();
351 $("canvas").remove();
352 $("[data-graph]").commits(graph_options);
353 });
354
355 $("#clear_filter").on("click", function() {
231 $("#clear_filter").on("click", function() {
356 var filter = {'repo_name': '${c.repo_name}'};
232 var filter = {'repo_name': '${c.repo_name}'};
357 window.location = pyroutes.url('changelog_home', filter);
233 window.location = pyroutes.url('changelog_home', filter);
@@ -394,7 +270,6 b''
394 }
270 }
395 }
271 }
396 });
272 });
397
398 $('#branch_filter').on('change', function(e){
273 $('#branch_filter').on('change', function(e){
399 var data = $('#branch_filter').select2('data');
274 var data = $('#branch_filter').select2('data');
400 var selected = data.text;
275 var selected = data.text;
@@ -408,33 +283,17 b''
408 window.location = pyroutes.url('changelog_home', filter);
283 window.location = pyroutes.url('changelog_home', filter);
409 });
284 });
410
285
411 // Determine max number of edges per row in graph
286 commitsController = new CommitsController();
412 var jsdata = $.parseJSON($("[data-graph]").attr('data-graph'));
287 % if not c.changelog_for_path:
413 var edgeCount = 1;
288 commitsController.reloadGraph();
414 $.each(jsdata, function(i, item){
289 % endif
415 $.each(item[2], function(key, value) {
416 if (value[1] > edgeCount){
417 edgeCount = value[1];
418 }
419 });
420 });
421 var x_step = Math.min(18, Math.floor(86 / edgeCount));
422 var graph_options = {
423 width: 100,
424 height: $("#changesets").height(),
425 x_step: x_step,
426 y_step: 42,
427 dotRadius: 3.5,
428 lineWidth: 2.5
429 };
430 $("[data-graph]").commits(graph_options);
431
290
432 });
291 });
433
292
434 </script>
293 </script>
435 %else:
294 </div>
295 % else:
436 ${_('There are no changes yet')}
296 ${_('There are no changes yet')}
437 %endif
297 % endif
438 </div>
439 </div>
298 </div>
440 </%def>
299 </%def>
@@ -1,11 +1,122 b''
1 ## small box that displays changed/added/removed details fetched by AJAX
1 ## small box that displays changed/added/removed details fetched by AJAX
2 <%namespace name="base" file="/base/base.mako"/>
3
4
5 % if c.prev_page:
6 <tr>
7 <td colspan="9" class="load-more-commits">
8 <a class="prev-commits" href="#loadPrevCommits" onclick="commitsController.loadPrev(this, ${c.prev_page}, '${c.branch_name}');return false">
9 ${_('load previous')}
10 </a>
11 </td>
12 </tr>
13 % endif
14
15 % for cnt,commit in enumerate(c.pagination):
16 <tr id="sha_${commit.raw_id}" class="changelogRow container ${'tablerow%s' % (cnt%2)}">
17
18 <td class="td-checkbox">
19 ${h.checkbox(commit.raw_id,class_="commit-range")}
20 </td>
21 <td class="td-status">
2
22
3 % if len(c.commit.affected_files) <= c.affected_files_cut_off:
23 %if c.statuses.get(commit.raw_id):
4 <span class="removed tooltip" title="<b>${h.tooltip(_('Removed'))}</b>${h.changed_tooltip(c.commit.removed)}">${len(c.commit.removed)}</span>
24 <div class="changeset-status-ico">
5 <span class="changed tooltip" title="<b>${h.tooltip(_('Changed'))}</b>${h.changed_tooltip(c.commit.changed)}">${len(c.commit.changed)}</span>
25 %if c.statuses.get(commit.raw_id)[2]:
6 <span class="added tooltip" title="<b>${h.tooltip(_('Added'))}</b>${h.changed_tooltip(c.commit.added)}">${len(c.commit.added)}</span>
26 <a class="tooltip" title="${_('Commit status: %s\nClick to open associated pull request #%s') % (h.commit_status_lbl(c.statuses.get(commit.raw_id)[0]), c.statuses.get(commit.raw_id)[2])}" href="${h.url('pullrequest_show',repo_name=c.statuses.get(commit.raw_id)[3],pull_request_id=c.statuses.get(commit.raw_id)[2])}">
7 % else:
27 <div class="${'flag_status %s' % c.statuses.get(commit.raw_id)[0]}"></div>
8 <span class="removed tooltip" title="${h.tooltip(_('Affected %s files') % len(c.commit.affected_files))}">!</span>
28 </a>
9 <span class="changed tooltip" title="${h.tooltip(_('Affected %s files') % len(c.commit.affected_files))}">!</span>
29 %else:
10 <span class="added tooltip" title="${h.tooltip(_('Affected %s files') % len(c.commit.affected_files))}">!</span>
30 <a class="tooltip" title="${_('Commit status: %s') % h.commit_status_lbl(c.statuses.get(commit.raw_id)[0])}" href="${h.url('changeset_home',repo_name=c.repo_name,revision=commit.raw_id,anchor='comment-%s' % c.comments[commit.raw_id][0].comment_id)}">
31 <div class="${'flag_status %s' % c.statuses.get(commit.raw_id)[0]}"></div>
32 </a>
33 %endif
34 </div>
35 %else:
36 <div class="tooltip flag_status not_reviewed" title="${_('Commit status: Not Reviewed')}"></div>
37 %endif
38 </td>
39 <td class="td-comments comments-col">
40 %if c.comments.get(commit.raw_id):
41 <a title="${_('Commit has comments')}" href="${h.url('changeset_home',repo_name=c.repo_name,revision=commit.raw_id,anchor='comment-%s' % c.comments[commit.raw_id][0].comment_id)}">
42 <i class="icon-comment"></i> ${len(c.comments[commit.raw_id])}
43 </a>
44 %endif
45 </td>
46 <td class="td-hash">
47 <code>
48 <a href="${h.url('changeset_home',repo_name=c.repo_name,revision=commit.raw_id)}">
49 <span class="commit_hash">${h.show_id(commit)}</span>
50 </a>
51 </code>
52 </td>
53 <td class="td-message expand_commit" data-commit-id="${commit.raw_id}" title="${_('Expand commit message')}" onclick="commitsController.expandCommit(this); return false">
54 <div class="show_more_col">
55 <i class="show_more"></i>&nbsp;
56 </div>
57 </td>
58 <td class="td-description mid">
59 <div class="log-container truncate-wrap">
60 <div class="message truncate" id="c-${commit.raw_id}">${h.urlify_commit_message(commit.message, c.repo_name)}</div>
61 </div>
62 </td>
63
64 <td class="td-time">
65 ${h.age_component(commit.date)}
66 </td>
67 <td class="td-user">
68 ${base.gravatar_with_user(commit.author)}
69 </td>
70
71 <td class="td-tags tags-col">
72 <div id="t-${commit.raw_id}">
73
74 ## merge
75 %if commit.merge:
76 <span class="tag mergetag">
77 <i class="icon-merge"></i>${_('merge')}
78 </span>
79 %endif
80
81 ## branch
82 %if commit.branch:
83 <span class="tag branchtag" title="${_('Branch %s') % commit.branch}">
84 <a href="${h.url('changelog_home',repo_name=c.repo_name,branch=commit.branch)}"><i class="icon-code-fork"></i>${h.shorter(commit.branch)}</a>
85 </span>
86 %endif
87
88 ## bookmarks
89 %if h.is_hg(c.rhodecode_repo):
90 %for book in commit.bookmarks:
91 <span class="tag booktag" title="${_('Bookmark %s') % book}">
92 <a href="${h.url('files_home',repo_name=c.repo_name,revision=commit.raw_id)}"><i class="icon-bookmark"></i>${h.shorter(book)}</a>
93 </span>
94 %endfor
95 %endif
96
97 ## tags
98 %for tag in commit.tags:
99 <span class="tag tagtag" title="${_('Tag %s') % tag}">
100 <a href="${h.url('files_home',repo_name=c.repo_name,revision=commit.raw_id)}"><i class="icon-tag"></i>${h.shorter(tag)}</a>
101 </span>
102 %endfor
103
104 </div>
105 </td>
106 </tr>
107 % endfor
108
109 % if c.next_page:
110 <tr>
111 <td colspan="9" class="load-more-commits">
112 <a class="next-commits" href="#loadNextCommits" onclick="commitsController.loadNext(this, ${c.next_page}, '${c.branch_name}');return false">
113 ${_('load next')}
114 </a>
115 </td>
116 </tr>
11 % endif
117 % endif
118 <tr class="chunk-graph-data" style="display:none"
119 data-graph='${c.graph_data|n}'
120 data-node='${c.prev_page}:${c.next_page}'
121 data-commits='${c.graph_commits|n}'>
122 </tr> No newline at end of file
@@ -14,12 +14,6 b''
14 </td>
14 </td>
15 <td class="td-message">
15 <td class="td-message">
16 <div class="log-container">
16 <div class="log-container">
17
18 %if cs.merge:
19 <span class="mergetag">
20 <i class="icon-merge"></i>${_('merge')}
21 </span>
22 %endif
23 <div class="message_history" title="${cs.message}">
17 <div class="message_history" title="${cs.message}">
24 <a href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id)}">
18 <a href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id)}">
25 ${h.shorter(cs.message, 75)}
19 ${h.shorter(cs.message, 75)}
General Comments 0
You need to be logged in to leave comments. Login now