##// END OF EJS Templates
diffs: update all sticky elements on dom changes to handle cases like:...
dan -
r3129:62991ed2 default
parent child Browse files
Show More
@@ -1,836 +1,836 b''
1 // # Copyright (C) 2010-2018 RhodeCode GmbH
1 // # Copyright (C) 2010-2018 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
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
4 // # it under the terms of the GNU Affero General Public License, version 3
5 // # (only), as published by the Free Software Foundation.
5 // # (only), as published by the Free Software Foundation.
6 // #
6 // #
7 // # This program is distributed in the hope that it will be useful,
7 // # This program is distributed in the hope that it will be useful,
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // # GNU General Public License for more details.
10 // # GNU General Public License for more details.
11 // #
11 // #
12 // # You should have received a copy of the GNU Affero General Public License
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/>.
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 // #
14 // #
15 // # This program is dual-licensed. If you wish to learn more about the
15 // # This program is dual-licensed. If you wish to learn more about the
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19 var firefoxAnchorFix = function() {
19 var firefoxAnchorFix = function() {
20 // hack to make anchor links behave properly on firefox, in our inline
20 // hack to make anchor links behave properly on firefox, in our inline
21 // comments generation when comments are injected firefox is misbehaving
21 // comments generation when comments are injected firefox is misbehaving
22 // when jumping to anchor links
22 // when jumping to anchor links
23 if (location.href.indexOf('#') > -1) {
23 if (location.href.indexOf('#') > -1) {
24 location.href += '';
24 location.href += '';
25 }
25 }
26 };
26 };
27
27
28 var linkifyComments = function(comments) {
28 var linkifyComments = function(comments) {
29 var firstCommentId = null;
29 var firstCommentId = null;
30 if (comments) {
30 if (comments) {
31 firstCommentId = $(comments[0]).data('comment-id');
31 firstCommentId = $(comments[0]).data('comment-id');
32 }
32 }
33
33
34 if (firstCommentId){
34 if (firstCommentId){
35 $('#inline-comments-counter').attr('href', '#comment-' + firstCommentId);
35 $('#inline-comments-counter').attr('href', '#comment-' + firstCommentId);
36 }
36 }
37 };
37 };
38
38
39 var bindToggleButtons = function() {
39 var bindToggleButtons = function() {
40 $('.comment-toggle').on('click', function() {
40 $('.comment-toggle').on('click', function() {
41 $(this).parent().nextUntil('tr.line').toggle('inline-comments');
41 $(this).parent().nextUntil('tr.line').toggle('inline-comments');
42 });
42 });
43 };
43 };
44
44
45
45
46
46
47 var _submitAjaxPOST = function(url, postData, successHandler, failHandler) {
47 var _submitAjaxPOST = function(url, postData, successHandler, failHandler) {
48 failHandler = failHandler || function() {};
48 failHandler = failHandler || function() {};
49 postData = toQueryString(postData);
49 postData = toQueryString(postData);
50 var request = $.ajax({
50 var request = $.ajax({
51 url: url,
51 url: url,
52 type: 'POST',
52 type: 'POST',
53 data: postData,
53 data: postData,
54 headers: {'X-PARTIAL-XHR': true}
54 headers: {'X-PARTIAL-XHR': true}
55 })
55 })
56 .done(function (data) {
56 .done(function (data) {
57 successHandler(data);
57 successHandler(data);
58 })
58 })
59 .fail(function (data, textStatus, errorThrown) {
59 .fail(function (data, textStatus, errorThrown) {
60 failHandler(data, textStatus, errorThrown)
60 failHandler(data, textStatus, errorThrown)
61 });
61 });
62 return request;
62 return request;
63 };
63 };
64
64
65
65
66
66
67
67
68 /* Comment form for main and inline comments */
68 /* Comment form for main and inline comments */
69 (function(mod) {
69 (function(mod) {
70
70
71 if (typeof exports == "object" && typeof module == "object") {
71 if (typeof exports == "object" && typeof module == "object") {
72 // CommonJS
72 // CommonJS
73 module.exports = mod();
73 module.exports = mod();
74 }
74 }
75 else {
75 else {
76 // Plain browser env
76 // Plain browser env
77 (this || window).CommentForm = mod();
77 (this || window).CommentForm = mod();
78 }
78 }
79
79
80 })(function() {
80 })(function() {
81 "use strict";
81 "use strict";
82
82
83 function CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId) {
83 function CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId) {
84 if (!(this instanceof CommentForm)) {
84 if (!(this instanceof CommentForm)) {
85 return new CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId);
85 return new CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId);
86 }
86 }
87
87
88 // bind the element instance to our Form
88 // bind the element instance to our Form
89 $(formElement).get(0).CommentForm = this;
89 $(formElement).get(0).CommentForm = this;
90
90
91 this.withLineNo = function(selector) {
91 this.withLineNo = function(selector) {
92 var lineNo = this.lineNo;
92 var lineNo = this.lineNo;
93 if (lineNo === undefined) {
93 if (lineNo === undefined) {
94 return selector
94 return selector
95 } else {
95 } else {
96 return selector + '_' + lineNo;
96 return selector + '_' + lineNo;
97 }
97 }
98 };
98 };
99
99
100 this.commitId = commitId;
100 this.commitId = commitId;
101 this.pullRequestId = pullRequestId;
101 this.pullRequestId = pullRequestId;
102 this.lineNo = lineNo;
102 this.lineNo = lineNo;
103 this.initAutocompleteActions = initAutocompleteActions;
103 this.initAutocompleteActions = initAutocompleteActions;
104
104
105 this.previewButton = this.withLineNo('#preview-btn');
105 this.previewButton = this.withLineNo('#preview-btn');
106 this.previewContainer = this.withLineNo('#preview-container');
106 this.previewContainer = this.withLineNo('#preview-container');
107
107
108 this.previewBoxSelector = this.withLineNo('#preview-box');
108 this.previewBoxSelector = this.withLineNo('#preview-box');
109
109
110 this.editButton = this.withLineNo('#edit-btn');
110 this.editButton = this.withLineNo('#edit-btn');
111 this.editContainer = this.withLineNo('#edit-container');
111 this.editContainer = this.withLineNo('#edit-container');
112 this.cancelButton = this.withLineNo('#cancel-btn');
112 this.cancelButton = this.withLineNo('#cancel-btn');
113 this.commentType = this.withLineNo('#comment_type');
113 this.commentType = this.withLineNo('#comment_type');
114
114
115 this.resolvesId = null;
115 this.resolvesId = null;
116 this.resolvesActionId = null;
116 this.resolvesActionId = null;
117
117
118 this.closesPr = '#close_pull_request';
118 this.closesPr = '#close_pull_request';
119
119
120 this.cmBox = this.withLineNo('#text');
120 this.cmBox = this.withLineNo('#text');
121 this.cm = initCommentBoxCodeMirror(this, this.cmBox, this.initAutocompleteActions);
121 this.cm = initCommentBoxCodeMirror(this, this.cmBox, this.initAutocompleteActions);
122
122
123 this.statusChange = this.withLineNo('#change_status');
123 this.statusChange = this.withLineNo('#change_status');
124
124
125 this.submitForm = formElement;
125 this.submitForm = formElement;
126 this.submitButton = $(this.submitForm).find('input[type="submit"]');
126 this.submitButton = $(this.submitForm).find('input[type="submit"]');
127 this.submitButtonText = this.submitButton.val();
127 this.submitButtonText = this.submitButton.val();
128
128
129 this.previewUrl = pyroutes.url('repo_commit_comment_preview',
129 this.previewUrl = pyroutes.url('repo_commit_comment_preview',
130 {'repo_name': templateContext.repo_name,
130 {'repo_name': templateContext.repo_name,
131 'commit_id': templateContext.commit_data.commit_id});
131 'commit_id': templateContext.commit_data.commit_id});
132
132
133 if (resolvesCommentId){
133 if (resolvesCommentId){
134 this.resolvesId = '#resolve_comment_{0}'.format(resolvesCommentId);
134 this.resolvesId = '#resolve_comment_{0}'.format(resolvesCommentId);
135 this.resolvesActionId = '#resolve_comment_action_{0}'.format(resolvesCommentId);
135 this.resolvesActionId = '#resolve_comment_action_{0}'.format(resolvesCommentId);
136 $(this.commentType).prop('disabled', true);
136 $(this.commentType).prop('disabled', true);
137 $(this.commentType).addClass('disabled');
137 $(this.commentType).addClass('disabled');
138
138
139 // disable select
139 // disable select
140 setTimeout(function() {
140 setTimeout(function() {
141 $(self.statusChange).select2('readonly', true);
141 $(self.statusChange).select2('readonly', true);
142 }, 10);
142 }, 10);
143
143
144 var resolvedInfo = (
144 var resolvedInfo = (
145 '<li class="resolve-action">' +
145 '<li class="resolve-action">' +
146 '<input type="hidden" id="resolve_comment_{0}" name="resolve_comment_{0}" value="{0}">' +
146 '<input type="hidden" id="resolve_comment_{0}" name="resolve_comment_{0}" value="{0}">' +
147 '<button id="resolve_comment_action_{0}" class="resolve-text btn btn-sm" onclick="return Rhodecode.comments.submitResolution({0})">{1} #{0}</button>' +
147 '<button id="resolve_comment_action_{0}" class="resolve-text btn btn-sm" onclick="return Rhodecode.comments.submitResolution({0})">{1} #{0}</button>' +
148 '</li>'
148 '</li>'
149 ).format(resolvesCommentId, _gettext('resolve comment'));
149 ).format(resolvesCommentId, _gettext('resolve comment'));
150 $(resolvedInfo).insertAfter($(this.commentType).parent());
150 $(resolvedInfo).insertAfter($(this.commentType).parent());
151 }
151 }
152
152
153 // based on commitId, or pullRequestId decide where do we submit
153 // based on commitId, or pullRequestId decide where do we submit
154 // out data
154 // out data
155 if (this.commitId){
155 if (this.commitId){
156 this.submitUrl = pyroutes.url('repo_commit_comment_create',
156 this.submitUrl = pyroutes.url('repo_commit_comment_create',
157 {'repo_name': templateContext.repo_name,
157 {'repo_name': templateContext.repo_name,
158 'commit_id': this.commitId});
158 'commit_id': this.commitId});
159 this.selfUrl = pyroutes.url('repo_commit',
159 this.selfUrl = pyroutes.url('repo_commit',
160 {'repo_name': templateContext.repo_name,
160 {'repo_name': templateContext.repo_name,
161 'commit_id': this.commitId});
161 'commit_id': this.commitId});
162
162
163 } else if (this.pullRequestId) {
163 } else if (this.pullRequestId) {
164 this.submitUrl = pyroutes.url('pullrequest_comment_create',
164 this.submitUrl = pyroutes.url('pullrequest_comment_create',
165 {'repo_name': templateContext.repo_name,
165 {'repo_name': templateContext.repo_name,
166 'pull_request_id': this.pullRequestId});
166 'pull_request_id': this.pullRequestId});
167 this.selfUrl = pyroutes.url('pullrequest_show',
167 this.selfUrl = pyroutes.url('pullrequest_show',
168 {'repo_name': templateContext.repo_name,
168 {'repo_name': templateContext.repo_name,
169 'pull_request_id': this.pullRequestId});
169 'pull_request_id': this.pullRequestId});
170
170
171 } else {
171 } else {
172 throw new Error(
172 throw new Error(
173 'CommentForm requires pullRequestId, or commitId to be specified.')
173 'CommentForm requires pullRequestId, or commitId to be specified.')
174 }
174 }
175
175
176 // FUNCTIONS and helpers
176 // FUNCTIONS and helpers
177 var self = this;
177 var self = this;
178
178
179 this.isInline = function(){
179 this.isInline = function(){
180 return this.lineNo && this.lineNo != 'general';
180 return this.lineNo && this.lineNo != 'general';
181 };
181 };
182
182
183 this.getCmInstance = function(){
183 this.getCmInstance = function(){
184 return this.cm
184 return this.cm
185 };
185 };
186
186
187 this.setPlaceholder = function(placeholder) {
187 this.setPlaceholder = function(placeholder) {
188 var cm = this.getCmInstance();
188 var cm = this.getCmInstance();
189 if (cm){
189 if (cm){
190 cm.setOption('placeholder', placeholder);
190 cm.setOption('placeholder', placeholder);
191 }
191 }
192 };
192 };
193
193
194 this.getCommentStatus = function() {
194 this.getCommentStatus = function() {
195 return $(this.submitForm).find(this.statusChange).val();
195 return $(this.submitForm).find(this.statusChange).val();
196 };
196 };
197 this.getCommentType = function() {
197 this.getCommentType = function() {
198 return $(this.submitForm).find(this.commentType).val();
198 return $(this.submitForm).find(this.commentType).val();
199 };
199 };
200
200
201 this.getResolvesId = function() {
201 this.getResolvesId = function() {
202 return $(this.submitForm).find(this.resolvesId).val() || null;
202 return $(this.submitForm).find(this.resolvesId).val() || null;
203 };
203 };
204
204
205 this.getClosePr = function() {
205 this.getClosePr = function() {
206 return $(this.submitForm).find(this.closesPr).val() || null;
206 return $(this.submitForm).find(this.closesPr).val() || null;
207 };
207 };
208
208
209 this.markCommentResolved = function(resolvedCommentId){
209 this.markCommentResolved = function(resolvedCommentId){
210 $('#comment-label-{0}'.format(resolvedCommentId)).find('.resolved').show();
210 $('#comment-label-{0}'.format(resolvedCommentId)).find('.resolved').show();
211 $('#comment-label-{0}'.format(resolvedCommentId)).find('.resolve').hide();
211 $('#comment-label-{0}'.format(resolvedCommentId)).find('.resolve').hide();
212 };
212 };
213
213
214 this.isAllowedToSubmit = function() {
214 this.isAllowedToSubmit = function() {
215 return !$(this.submitButton).prop('disabled');
215 return !$(this.submitButton).prop('disabled');
216 };
216 };
217
217
218 this.initStatusChangeSelector = function(){
218 this.initStatusChangeSelector = function(){
219 var formatChangeStatus = function(state, escapeMarkup) {
219 var formatChangeStatus = function(state, escapeMarkup) {
220 var originalOption = state.element;
220 var originalOption = state.element;
221 return '<div class="flag_status ' + $(originalOption).data('status') + ' pull-left"></div>' +
221 return '<div class="flag_status ' + $(originalOption).data('status') + ' pull-left"></div>' +
222 '<span>' + escapeMarkup(state.text) + '</span>';
222 '<span>' + escapeMarkup(state.text) + '</span>';
223 };
223 };
224 var formatResult = function(result, container, query, escapeMarkup) {
224 var formatResult = function(result, container, query, escapeMarkup) {
225 return formatChangeStatus(result, escapeMarkup);
225 return formatChangeStatus(result, escapeMarkup);
226 };
226 };
227
227
228 var formatSelection = function(data, container, escapeMarkup) {
228 var formatSelection = function(data, container, escapeMarkup) {
229 return formatChangeStatus(data, escapeMarkup);
229 return formatChangeStatus(data, escapeMarkup);
230 };
230 };
231
231
232 $(this.submitForm).find(this.statusChange).select2({
232 $(this.submitForm).find(this.statusChange).select2({
233 placeholder: _gettext('Status Review'),
233 placeholder: _gettext('Status Review'),
234 formatResult: formatResult,
234 formatResult: formatResult,
235 formatSelection: formatSelection,
235 formatSelection: formatSelection,
236 containerCssClass: "drop-menu status_box_menu",
236 containerCssClass: "drop-menu status_box_menu",
237 dropdownCssClass: "drop-menu-dropdown",
237 dropdownCssClass: "drop-menu-dropdown",
238 dropdownAutoWidth: true,
238 dropdownAutoWidth: true,
239 minimumResultsForSearch: -1
239 minimumResultsForSearch: -1
240 });
240 });
241 $(this.submitForm).find(this.statusChange).on('change', function() {
241 $(this.submitForm).find(this.statusChange).on('change', function() {
242 var status = self.getCommentStatus();
242 var status = self.getCommentStatus();
243
243
244 if (status && !self.isInline()) {
244 if (status && !self.isInline()) {
245 $(self.submitButton).prop('disabled', false);
245 $(self.submitButton).prop('disabled', false);
246 }
246 }
247
247
248 var placeholderText = _gettext('Comment text will be set automatically based on currently selected status ({0}) ...').format(status);
248 var placeholderText = _gettext('Comment text will be set automatically based on currently selected status ({0}) ...').format(status);
249 self.setPlaceholder(placeholderText)
249 self.setPlaceholder(placeholderText)
250 })
250 })
251 };
251 };
252
252
253 // reset the comment form into it's original state
253 // reset the comment form into it's original state
254 this.resetCommentFormState = function(content) {
254 this.resetCommentFormState = function(content) {
255 content = content || '';
255 content = content || '';
256
256
257 $(this.editContainer).show();
257 $(this.editContainer).show();
258 $(this.editButton).parent().addClass('active');
258 $(this.editButton).parent().addClass('active');
259
259
260 $(this.previewContainer).hide();
260 $(this.previewContainer).hide();
261 $(this.previewButton).parent().removeClass('active');
261 $(this.previewButton).parent().removeClass('active');
262
262
263 this.setActionButtonsDisabled(true);
263 this.setActionButtonsDisabled(true);
264 self.cm.setValue(content);
264 self.cm.setValue(content);
265 self.cm.setOption("readOnly", false);
265 self.cm.setOption("readOnly", false);
266
266
267 if (this.resolvesId) {
267 if (this.resolvesId) {
268 // destroy the resolve action
268 // destroy the resolve action
269 $(this.resolvesId).parent().remove();
269 $(this.resolvesId).parent().remove();
270 }
270 }
271 // reset closingPR flag
271 // reset closingPR flag
272 $('.close-pr-input').remove();
272 $('.close-pr-input').remove();
273
273
274 $(this.statusChange).select2('readonly', false);
274 $(this.statusChange).select2('readonly', false);
275 };
275 };
276
276
277 this.globalSubmitSuccessCallback = function(){
277 this.globalSubmitSuccessCallback = function(){
278 // default behaviour is to call GLOBAL hook, if it's registered.
278 // default behaviour is to call GLOBAL hook, if it's registered.
279 if (window.commentFormGlobalSubmitSuccessCallback !== undefined){
279 if (window.commentFormGlobalSubmitSuccessCallback !== undefined){
280 commentFormGlobalSubmitSuccessCallback()
280 commentFormGlobalSubmitSuccessCallback()
281 }
281 }
282 };
282 };
283
283
284 this.submitAjaxPOST = function(url, postData, successHandler, failHandler) {
284 this.submitAjaxPOST = function(url, postData, successHandler, failHandler) {
285 return _submitAjaxPOST(url, postData, successHandler, failHandler);
285 return _submitAjaxPOST(url, postData, successHandler, failHandler);
286 };
286 };
287
287
288 // overwrite a submitHandler, we need to do it for inline comments
288 // overwrite a submitHandler, we need to do it for inline comments
289 this.setHandleFormSubmit = function(callback) {
289 this.setHandleFormSubmit = function(callback) {
290 this.handleFormSubmit = callback;
290 this.handleFormSubmit = callback;
291 };
291 };
292
292
293 // overwrite a submitSuccessHandler
293 // overwrite a submitSuccessHandler
294 this.setGlobalSubmitSuccessCallback = function(callback) {
294 this.setGlobalSubmitSuccessCallback = function(callback) {
295 this.globalSubmitSuccessCallback = callback;
295 this.globalSubmitSuccessCallback = callback;
296 };
296 };
297
297
298 // default handler for for submit for main comments
298 // default handler for for submit for main comments
299 this.handleFormSubmit = function() {
299 this.handleFormSubmit = function() {
300 var text = self.cm.getValue();
300 var text = self.cm.getValue();
301 var status = self.getCommentStatus();
301 var status = self.getCommentStatus();
302 var commentType = self.getCommentType();
302 var commentType = self.getCommentType();
303 var resolvesCommentId = self.getResolvesId();
303 var resolvesCommentId = self.getResolvesId();
304 var closePullRequest = self.getClosePr();
304 var closePullRequest = self.getClosePr();
305
305
306 if (text === "" && !status) {
306 if (text === "" && !status) {
307 return;
307 return;
308 }
308 }
309
309
310 var excludeCancelBtn = false;
310 var excludeCancelBtn = false;
311 var submitEvent = true;
311 var submitEvent = true;
312 self.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
312 self.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
313 self.cm.setOption("readOnly", true);
313 self.cm.setOption("readOnly", true);
314
314
315 var postData = {
315 var postData = {
316 'text': text,
316 'text': text,
317 'changeset_status': status,
317 'changeset_status': status,
318 'comment_type': commentType,
318 'comment_type': commentType,
319 'csrf_token': CSRF_TOKEN
319 'csrf_token': CSRF_TOKEN
320 };
320 };
321
321
322 if (resolvesCommentId) {
322 if (resolvesCommentId) {
323 postData['resolves_comment_id'] = resolvesCommentId;
323 postData['resolves_comment_id'] = resolvesCommentId;
324 }
324 }
325
325
326 if (closePullRequest) {
326 if (closePullRequest) {
327 postData['close_pull_request'] = true;
327 postData['close_pull_request'] = true;
328 }
328 }
329
329
330 var submitSuccessCallback = function(o) {
330 var submitSuccessCallback = function(o) {
331 // reload page if we change status for single commit.
331 // reload page if we change status for single commit.
332 if (status && self.commitId) {
332 if (status && self.commitId) {
333 location.reload(true);
333 location.reload(true);
334 } else {
334 } else {
335 $('#injected_page_comments').append(o.rendered_text);
335 $('#injected_page_comments').append(o.rendered_text);
336 self.resetCommentFormState();
336 self.resetCommentFormState();
337 timeagoActivate();
337 timeagoActivate();
338
338
339 // mark visually which comment was resolved
339 // mark visually which comment was resolved
340 if (resolvesCommentId) {
340 if (resolvesCommentId) {
341 self.markCommentResolved(resolvesCommentId);
341 self.markCommentResolved(resolvesCommentId);
342 }
342 }
343 }
343 }
344
344
345 // run global callback on submit
345 // run global callback on submit
346 self.globalSubmitSuccessCallback();
346 self.globalSubmitSuccessCallback();
347
347
348 };
348 };
349 var submitFailCallback = function(data) {
349 var submitFailCallback = function(data) {
350 alert(
350 alert(
351 "Error while submitting comment.\n" +
351 "Error while submitting comment.\n" +
352 "Error code {0} ({1}).".format(data.status, data.statusText)
352 "Error code {0} ({1}).".format(data.status, data.statusText)
353 );
353 );
354 self.resetCommentFormState(text);
354 self.resetCommentFormState(text);
355 };
355 };
356 self.submitAjaxPOST(
356 self.submitAjaxPOST(
357 self.submitUrl, postData, submitSuccessCallback, submitFailCallback);
357 self.submitUrl, postData, submitSuccessCallback, submitFailCallback);
358 };
358 };
359
359
360 this.previewSuccessCallback = function(o) {
360 this.previewSuccessCallback = function(o) {
361 $(self.previewBoxSelector).html(o);
361 $(self.previewBoxSelector).html(o);
362 $(self.previewBoxSelector).removeClass('unloaded');
362 $(self.previewBoxSelector).removeClass('unloaded');
363
363
364 // swap buttons, making preview active
364 // swap buttons, making preview active
365 $(self.previewButton).parent().addClass('active');
365 $(self.previewButton).parent().addClass('active');
366 $(self.editButton).parent().removeClass('active');
366 $(self.editButton).parent().removeClass('active');
367
367
368 // unlock buttons
368 // unlock buttons
369 self.setActionButtonsDisabled(false);
369 self.setActionButtonsDisabled(false);
370 };
370 };
371
371
372 this.setActionButtonsDisabled = function(state, excludeCancelBtn, submitEvent) {
372 this.setActionButtonsDisabled = function(state, excludeCancelBtn, submitEvent) {
373 excludeCancelBtn = excludeCancelBtn || false;
373 excludeCancelBtn = excludeCancelBtn || false;
374 submitEvent = submitEvent || false;
374 submitEvent = submitEvent || false;
375
375
376 $(this.editButton).prop('disabled', state);
376 $(this.editButton).prop('disabled', state);
377 $(this.previewButton).prop('disabled', state);
377 $(this.previewButton).prop('disabled', state);
378
378
379 if (!excludeCancelBtn) {
379 if (!excludeCancelBtn) {
380 $(this.cancelButton).prop('disabled', state);
380 $(this.cancelButton).prop('disabled', state);
381 }
381 }
382
382
383 var submitState = state;
383 var submitState = state;
384 if (!submitEvent && this.getCommentStatus() && !self.isInline()) {
384 if (!submitEvent && this.getCommentStatus() && !self.isInline()) {
385 // if the value of commit review status is set, we allow
385 // if the value of commit review status is set, we allow
386 // submit button, but only on Main form, isInline means inline
386 // submit button, but only on Main form, isInline means inline
387 submitState = false
387 submitState = false
388 }
388 }
389
389
390 $(this.submitButton).prop('disabled', submitState);
390 $(this.submitButton).prop('disabled', submitState);
391 if (submitEvent) {
391 if (submitEvent) {
392 $(this.submitButton).val(_gettext('Submitting...'));
392 $(this.submitButton).val(_gettext('Submitting...'));
393 } else {
393 } else {
394 $(this.submitButton).val(this.submitButtonText);
394 $(this.submitButton).val(this.submitButtonText);
395 }
395 }
396
396
397 };
397 };
398
398
399 // lock preview/edit/submit buttons on load, but exclude cancel button
399 // lock preview/edit/submit buttons on load, but exclude cancel button
400 var excludeCancelBtn = true;
400 var excludeCancelBtn = true;
401 this.setActionButtonsDisabled(true, excludeCancelBtn);
401 this.setActionButtonsDisabled(true, excludeCancelBtn);
402
402
403 // anonymous users don't have access to initialized CM instance
403 // anonymous users don't have access to initialized CM instance
404 if (this.cm !== undefined){
404 if (this.cm !== undefined){
405 this.cm.on('change', function(cMirror) {
405 this.cm.on('change', function(cMirror) {
406 if (cMirror.getValue() === "") {
406 if (cMirror.getValue() === "") {
407 self.setActionButtonsDisabled(true, excludeCancelBtn)
407 self.setActionButtonsDisabled(true, excludeCancelBtn)
408 } else {
408 } else {
409 self.setActionButtonsDisabled(false, excludeCancelBtn)
409 self.setActionButtonsDisabled(false, excludeCancelBtn)
410 }
410 }
411 });
411 });
412 }
412 }
413
413
414 $(this.editButton).on('click', function(e) {
414 $(this.editButton).on('click', function(e) {
415 e.preventDefault();
415 e.preventDefault();
416
416
417 $(self.previewButton).parent().removeClass('active');
417 $(self.previewButton).parent().removeClass('active');
418 $(self.previewContainer).hide();
418 $(self.previewContainer).hide();
419
419
420 $(self.editButton).parent().addClass('active');
420 $(self.editButton).parent().addClass('active');
421 $(self.editContainer).show();
421 $(self.editContainer).show();
422
422
423 });
423 });
424
424
425 $(this.previewButton).on('click', function(e) {
425 $(this.previewButton).on('click', function(e) {
426 e.preventDefault();
426 e.preventDefault();
427 var text = self.cm.getValue();
427 var text = self.cm.getValue();
428
428
429 if (text === "") {
429 if (text === "") {
430 return;
430 return;
431 }
431 }
432
432
433 var postData = {
433 var postData = {
434 'text': text,
434 'text': text,
435 'renderer': templateContext.visual.default_renderer,
435 'renderer': templateContext.visual.default_renderer,
436 'csrf_token': CSRF_TOKEN
436 'csrf_token': CSRF_TOKEN
437 };
437 };
438
438
439 // lock ALL buttons on preview
439 // lock ALL buttons on preview
440 self.setActionButtonsDisabled(true);
440 self.setActionButtonsDisabled(true);
441
441
442 $(self.previewBoxSelector).addClass('unloaded');
442 $(self.previewBoxSelector).addClass('unloaded');
443 $(self.previewBoxSelector).html(_gettext('Loading ...'));
443 $(self.previewBoxSelector).html(_gettext('Loading ...'));
444
444
445 $(self.editContainer).hide();
445 $(self.editContainer).hide();
446 $(self.previewContainer).show();
446 $(self.previewContainer).show();
447
447
448 // by default we reset state of comment preserving the text
448 // by default we reset state of comment preserving the text
449 var previewFailCallback = function(data){
449 var previewFailCallback = function(data){
450 alert(
450 alert(
451 "Error while preview of comment.\n" +
451 "Error while preview of comment.\n" +
452 "Error code {0} ({1}).".format(data.status, data.statusText)
452 "Error code {0} ({1}).".format(data.status, data.statusText)
453 );
453 );
454 self.resetCommentFormState(text)
454 self.resetCommentFormState(text)
455 };
455 };
456 self.submitAjaxPOST(
456 self.submitAjaxPOST(
457 self.previewUrl, postData, self.previewSuccessCallback,
457 self.previewUrl, postData, self.previewSuccessCallback,
458 previewFailCallback);
458 previewFailCallback);
459
459
460 $(self.previewButton).parent().addClass('active');
460 $(self.previewButton).parent().addClass('active');
461 $(self.editButton).parent().removeClass('active');
461 $(self.editButton).parent().removeClass('active');
462 });
462 });
463
463
464 $(this.submitForm).submit(function(e) {
464 $(this.submitForm).submit(function(e) {
465 e.preventDefault();
465 e.preventDefault();
466 var allowedToSubmit = self.isAllowedToSubmit();
466 var allowedToSubmit = self.isAllowedToSubmit();
467 if (!allowedToSubmit){
467 if (!allowedToSubmit){
468 return false;
468 return false;
469 }
469 }
470 self.handleFormSubmit();
470 self.handleFormSubmit();
471 });
471 });
472
472
473 }
473 }
474
474
475 return CommentForm;
475 return CommentForm;
476 });
476 });
477
477
478 /* comments controller */
478 /* comments controller */
479 var CommentsController = function() {
479 var CommentsController = function() {
480 var mainComment = '#text';
480 var mainComment = '#text';
481 var self = this;
481 var self = this;
482
482
483 this.cancelComment = function(node) {
483 this.cancelComment = function(node) {
484 var $node = $(node);
484 var $node = $(node);
485 var $td = $node.closest('td');
485 var $td = $node.closest('td');
486 $node.closest('.comment-inline-form').remove();
486 $node.closest('.comment-inline-form').remove();
487 return false;
487 return false;
488 };
488 };
489
489
490 this.getLineNumber = function(node) {
490 this.getLineNumber = function(node) {
491 var $node = $(node);
491 var $node = $(node);
492 var lineNo = $node.closest('td').attr('data-line-no');
492 var lineNo = $node.closest('td').attr('data-line-no');
493 if (lineNo === undefined && $node.data('commentInline')){
493 if (lineNo === undefined && $node.data('commentInline')){
494 lineNo = $node.data('commentLineNo')
494 lineNo = $node.data('commentLineNo')
495 }
495 }
496
496
497 return lineNo
497 return lineNo
498 };
498 };
499
499
500 this.scrollToComment = function(node, offset, outdated) {
500 this.scrollToComment = function(node, offset, outdated) {
501 if (offset === undefined) {
501 if (offset === undefined) {
502 offset = 0;
502 offset = 0;
503 }
503 }
504 var outdated = outdated || false;
504 var outdated = outdated || false;
505 var klass = outdated ? 'div.comment-outdated' : 'div.comment-current';
505 var klass = outdated ? 'div.comment-outdated' : 'div.comment-current';
506
506
507 if (!node) {
507 if (!node) {
508 node = $('.comment-selected');
508 node = $('.comment-selected');
509 if (!node.length) {
509 if (!node.length) {
510 node = $('comment-current')
510 node = $('comment-current')
511 }
511 }
512 }
512 }
513 $wrapper = $(node).closest('div.comment');
513 $wrapper = $(node).closest('div.comment');
514 $comment = $(node).closest(klass);
514 $comment = $(node).closest(klass);
515 $comments = $(klass);
515 $comments = $(klass);
516
516
517 // show hidden comment when referenced.
517 // show hidden comment when referenced.
518 if (!$wrapper.is(':visible')){
518 if (!$wrapper.is(':visible')){
519 $wrapper.show();
519 $wrapper.show();
520 }
520 }
521
521
522 $('.comment-selected').removeClass('comment-selected');
522 $('.comment-selected').removeClass('comment-selected');
523
523
524 var nextIdx = $(klass).index($comment) + offset;
524 var nextIdx = $(klass).index($comment) + offset;
525 if (nextIdx >= $comments.length) {
525 if (nextIdx >= $comments.length) {
526 nextIdx = 0;
526 nextIdx = 0;
527 }
527 }
528 var $next = $(klass).eq(nextIdx);
528 var $next = $(klass).eq(nextIdx);
529
529
530 var $cb = $next.closest('.cb');
530 var $cb = $next.closest('.cb');
531 $cb.removeClass('cb-collapsed');
531 $cb.removeClass('cb-collapsed');
532
532
533 var $filediffCollapseState = $cb.closest('.filediff').prev();
533 var $filediffCollapseState = $cb.closest('.filediff').prev();
534 $filediffCollapseState.prop('checked', false);
534 $filediffCollapseState.prop('checked', false);
535 $next.addClass('comment-selected');
535 $next.addClass('comment-selected');
536 scrollToElement($next);
536 scrollToElement($next);
537 return false;
537 return false;
538 };
538 };
539
539
540 this.nextComment = function(node) {
540 this.nextComment = function(node) {
541 return self.scrollToComment(node, 1);
541 return self.scrollToComment(node, 1);
542 };
542 };
543
543
544 this.prevComment = function(node) {
544 this.prevComment = function(node) {
545 return self.scrollToComment(node, -1);
545 return self.scrollToComment(node, -1);
546 };
546 };
547
547
548 this.nextOutdatedComment = function(node) {
548 this.nextOutdatedComment = function(node) {
549 return self.scrollToComment(node, 1, true);
549 return self.scrollToComment(node, 1, true);
550 };
550 };
551
551
552 this.prevOutdatedComment = function(node) {
552 this.prevOutdatedComment = function(node) {
553 return self.scrollToComment(node, -1, true);
553 return self.scrollToComment(node, -1, true);
554 };
554 };
555
555
556 this.deleteComment = function(node) {
556 this.deleteComment = function(node) {
557 if (!confirm(_gettext('Delete this comment?'))) {
557 if (!confirm(_gettext('Delete this comment?'))) {
558 return false;
558 return false;
559 }
559 }
560 var $node = $(node);
560 var $node = $(node);
561 var $td = $node.closest('td');
561 var $td = $node.closest('td');
562 var $comment = $node.closest('.comment');
562 var $comment = $node.closest('.comment');
563 var comment_id = $comment.attr('data-comment-id');
563 var comment_id = $comment.attr('data-comment-id');
564 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id);
564 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id);
565 var postData = {
565 var postData = {
566 'csrf_token': CSRF_TOKEN
566 'csrf_token': CSRF_TOKEN
567 };
567 };
568
568
569 $comment.addClass('comment-deleting');
569 $comment.addClass('comment-deleting');
570 $comment.hide('fast');
570 $comment.hide('fast');
571
571
572 var success = function(response) {
572 var success = function(response) {
573 $comment.remove();
573 $comment.remove();
574 return false;
574 return false;
575 };
575 };
576 var failure = function(data, textStatus, xhr) {
576 var failure = function(data, textStatus, xhr) {
577 alert("error processing request: " + textStatus);
577 alert("error processing request: " + textStatus);
578 $comment.show('fast');
578 $comment.show('fast');
579 $comment.removeClass('comment-deleting');
579 $comment.removeClass('comment-deleting');
580 return false;
580 return false;
581 };
581 };
582 ajaxPOST(url, postData, success, failure);
582 ajaxPOST(url, postData, success, failure);
583 };
583 };
584
584
585 this.toggleWideMode = function (node) {
585 this.toggleWideMode = function (node) {
586 if ($('#content').hasClass('wrapper')) {
586 if ($('#content').hasClass('wrapper')) {
587 $('#content').removeClass("wrapper");
587 $('#content').removeClass("wrapper");
588 $('#content').addClass("wide-mode-wrapper");
588 $('#content').addClass("wide-mode-wrapper");
589 $(node).addClass('btn-success');
589 $(node).addClass('btn-success');
590 } else {
590 } else {
591 $('#content').removeClass("wide-mode-wrapper");
591 $('#content').removeClass("wide-mode-wrapper");
592 $('#content').addClass("wrapper");
592 $('#content').addClass("wrapper");
593 $(node).removeClass('btn-success');
593 $(node).removeClass('btn-success');
594 }
594 }
595 return false;
595 return false;
596 };
596 };
597
597
598 this.toggleComments = function(node, show) {
598 this.toggleComments = function(node, show) {
599 var $filediff = $(node).closest('.filediff');
599 var $filediff = $(node).closest('.filediff');
600 if (show === true) {
600 if (show === true) {
601 $filediff.removeClass('hide-comments');
601 $filediff.removeClass('hide-comments');
602 } else if (show === false) {
602 } else if (show === false) {
603 $filediff.find('.hide-line-comments').removeClass('hide-line-comments');
603 $filediff.find('.hide-line-comments').removeClass('hide-line-comments');
604 $filediff.addClass('hide-comments');
604 $filediff.addClass('hide-comments');
605 } else {
605 } else {
606 $filediff.find('.hide-line-comments').removeClass('hide-line-comments');
606 $filediff.find('.hide-line-comments').removeClass('hide-line-comments');
607 $filediff.toggleClass('hide-comments');
607 $filediff.toggleClass('hide-comments');
608 }
608 }
609 return false;
609 return false;
610 };
610 };
611
611
612 this.toggleLineComments = function(node) {
612 this.toggleLineComments = function(node) {
613 self.toggleComments(node, true);
613 self.toggleComments(node, true);
614 var $node = $(node);
614 var $node = $(node);
615 // mark outdated comments as visible before the toggle;
615 // mark outdated comments as visible before the toggle;
616 $(node.closest('tr')).find('.comment-outdated').show();
616 $(node.closest('tr')).find('.comment-outdated').show();
617 $node.closest('tr').toggleClass('hide-line-comments');
617 $node.closest('tr').toggleClass('hide-line-comments');
618 };
618 };
619
619
620 this.createCommentForm = function(formElement, lineno, placeholderText, initAutocompleteActions, resolvesCommentId){
620 this.createCommentForm = function(formElement, lineno, placeholderText, initAutocompleteActions, resolvesCommentId){
621 var pullRequestId = templateContext.pull_request_data.pull_request_id;
621 var pullRequestId = templateContext.pull_request_data.pull_request_id;
622 var commitId = templateContext.commit_data.commit_id;
622 var commitId = templateContext.commit_data.commit_id;
623
623
624 var commentForm = new CommentForm(
624 var commentForm = new CommentForm(
625 formElement, commitId, pullRequestId, lineno, initAutocompleteActions, resolvesCommentId);
625 formElement, commitId, pullRequestId, lineno, initAutocompleteActions, resolvesCommentId);
626 var cm = commentForm.getCmInstance();
626 var cm = commentForm.getCmInstance();
627
627
628 if (resolvesCommentId){
628 if (resolvesCommentId){
629 var placeholderText = _gettext('Leave a comment, or click resolve button to resolve TODO comment #{0}').format(resolvesCommentId);
629 var placeholderText = _gettext('Leave a comment, or click resolve button to resolve TODO comment #{0}').format(resolvesCommentId);
630 }
630 }
631
631
632 setTimeout(function() {
632 setTimeout(function() {
633 // callbacks
633 // callbacks
634 if (cm !== undefined) {
634 if (cm !== undefined) {
635 commentForm.setPlaceholder(placeholderText);
635 commentForm.setPlaceholder(placeholderText);
636 if (commentForm.isInline()) {
636 if (commentForm.isInline()) {
637 cm.focus();
637 cm.focus();
638 cm.refresh();
638 cm.refresh();
639 }
639 }
640 }
640 }
641 }, 10);
641 }, 10);
642
642
643 // trigger scrolldown to the resolve comment, since it might be away
643 // trigger scrolldown to the resolve comment, since it might be away
644 // from the clicked
644 // from the clicked
645 if (resolvesCommentId){
645 if (resolvesCommentId){
646 var actionNode = $(commentForm.resolvesActionId).offset();
646 var actionNode = $(commentForm.resolvesActionId).offset();
647
647
648 setTimeout(function() {
648 setTimeout(function() {
649 if (actionNode) {
649 if (actionNode) {
650 $('body, html').animate({scrollTop: actionNode.top}, 10);
650 $('body, html').animate({scrollTop: actionNode.top}, 10);
651 }
651 }
652 }, 100);
652 }, 100);
653 }
653 }
654
654
655 return commentForm;
655 return commentForm;
656 };
656 };
657
657
658 this.createGeneralComment = function (lineNo, placeholderText, resolvesCommentId) {
658 this.createGeneralComment = function (lineNo, placeholderText, resolvesCommentId) {
659
659
660 var tmpl = $('#cb-comment-general-form-template').html();
660 var tmpl = $('#cb-comment-general-form-template').html();
661 tmpl = tmpl.format(null, 'general');
661 tmpl = tmpl.format(null, 'general');
662 var $form = $(tmpl);
662 var $form = $(tmpl);
663
663
664 var $formPlaceholder = $('#cb-comment-general-form-placeholder');
664 var $formPlaceholder = $('#cb-comment-general-form-placeholder');
665 var curForm = $formPlaceholder.find('form');
665 var curForm = $formPlaceholder.find('form');
666 if (curForm){
666 if (curForm){
667 curForm.remove();
667 curForm.remove();
668 }
668 }
669 $formPlaceholder.append($form);
669 $formPlaceholder.append($form);
670
670
671 var _form = $($form[0]);
671 var _form = $($form[0]);
672 var autocompleteActions = ['approve', 'reject', 'as_note', 'as_todo'];
672 var autocompleteActions = ['approve', 'reject', 'as_note', 'as_todo'];
673 var commentForm = this.createCommentForm(
673 var commentForm = this.createCommentForm(
674 _form, lineNo, placeholderText, autocompleteActions, resolvesCommentId);
674 _form, lineNo, placeholderText, autocompleteActions, resolvesCommentId);
675 commentForm.initStatusChangeSelector();
675 commentForm.initStatusChangeSelector();
676
676
677 return commentForm;
677 return commentForm;
678 };
678 };
679
679
680 this.createComment = function(node, resolutionComment) {
680 this.createComment = function(node, resolutionComment) {
681 var resolvesCommentId = resolutionComment || null;
681 var resolvesCommentId = resolutionComment || null;
682 var $node = $(node);
682 var $node = $(node);
683 var $td = $node.closest('td');
683 var $td = $node.closest('td');
684 var $form = $td.find('.comment-inline-form');
684 var $form = $td.find('.comment-inline-form');
685
685
686 if (!$form.length) {
686 if (!$form.length) {
687
687
688 var $filediff = $node.closest('.filediff');
688 var $filediff = $node.closest('.filediff');
689 $filediff.removeClass('hide-comments');
689 $filediff.removeClass('hide-comments');
690 var f_path = $filediff.attr('data-f-path');
690 var f_path = $filediff.attr('data-f-path');
691 var lineno = self.getLineNumber(node);
691 var lineno = self.getLineNumber(node);
692 // create a new HTML from template
692 // create a new HTML from template
693 var tmpl = $('#cb-comment-inline-form-template').html();
693 var tmpl = $('#cb-comment-inline-form-template').html();
694 tmpl = tmpl.format(escapeHtml(f_path), lineno);
694 tmpl = tmpl.format(escapeHtml(f_path), lineno);
695 $form = $(tmpl);
695 $form = $(tmpl);
696
696
697 var $comments = $td.find('.inline-comments');
697 var $comments = $td.find('.inline-comments');
698 if (!$comments.length) {
698 if (!$comments.length) {
699 $comments = $(
699 $comments = $(
700 $('#cb-comments-inline-container-template').html());
700 $('#cb-comments-inline-container-template').html());
701 $td.append($comments);
701 $td.append($comments);
702 }
702 }
703
703
704 $td.find('.cb-comment-add-button').before($form);
704 $td.find('.cb-comment-add-button').before($form);
705
705
706 var placeholderText = _gettext('Leave a comment on line {0}.').format(lineno);
706 var placeholderText = _gettext('Leave a comment on line {0}.').format(lineno);
707 var _form = $($form[0]).find('form');
707 var _form = $($form[0]).find('form');
708 var autocompleteActions = ['as_note', 'as_todo'];
708 var autocompleteActions = ['as_note', 'as_todo'];
709 var commentForm = this.createCommentForm(
709 var commentForm = this.createCommentForm(
710 _form, lineno, placeholderText, autocompleteActions, resolvesCommentId);
710 _form, lineno, placeholderText, autocompleteActions, resolvesCommentId);
711
711
712 $.Topic('/ui/plugins/code/comment_form_built').prepareOrPublish({
712 $.Topic('/ui/plugins/code/comment_form_built').prepareOrPublish({
713 form: _form,
713 form: _form,
714 parent: $td[0],
714 parent: $td[0],
715 lineno: lineno,
715 lineno: lineno,
716 f_path: f_path}
716 f_path: f_path}
717 );
717 );
718
718
719 // set a CUSTOM submit handler for inline comments.
719 // set a CUSTOM submit handler for inline comments.
720 commentForm.setHandleFormSubmit(function(o) {
720 commentForm.setHandleFormSubmit(function(o) {
721 var text = commentForm.cm.getValue();
721 var text = commentForm.cm.getValue();
722 var commentType = commentForm.getCommentType();
722 var commentType = commentForm.getCommentType();
723 var resolvesCommentId = commentForm.getResolvesId();
723 var resolvesCommentId = commentForm.getResolvesId();
724
724
725 if (text === "") {
725 if (text === "") {
726 return;
726 return;
727 }
727 }
728
728
729 if (lineno === undefined) {
729 if (lineno === undefined) {
730 alert('missing line !');
730 alert('missing line !');
731 return;
731 return;
732 }
732 }
733 if (f_path === undefined) {
733 if (f_path === undefined) {
734 alert('missing file path !');
734 alert('missing file path !');
735 return;
735 return;
736 }
736 }
737
737
738 var excludeCancelBtn = false;
738 var excludeCancelBtn = false;
739 var submitEvent = true;
739 var submitEvent = true;
740 commentForm.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
740 commentForm.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
741 commentForm.cm.setOption("readOnly", true);
741 commentForm.cm.setOption("readOnly", true);
742 var postData = {
742 var postData = {
743 'text': text,
743 'text': text,
744 'f_path': f_path,
744 'f_path': f_path,
745 'line': lineno,
745 'line': lineno,
746 'comment_type': commentType,
746 'comment_type': commentType,
747 'csrf_token': CSRF_TOKEN
747 'csrf_token': CSRF_TOKEN
748 };
748 };
749 if (resolvesCommentId){
749 if (resolvesCommentId){
750 postData['resolves_comment_id'] = resolvesCommentId;
750 postData['resolves_comment_id'] = resolvesCommentId;
751 }
751 }
752
752
753 var submitSuccessCallback = function(json_data) {
753 var submitSuccessCallback = function(json_data) {
754 $form.remove();
754 $form.remove();
755 try {
755 try {
756 var html = json_data.rendered_text;
756 var html = json_data.rendered_text;
757 var lineno = json_data.line_no;
757 var lineno = json_data.line_no;
758 var target_id = json_data.target_id;
758 var target_id = json_data.target_id;
759
759
760 $comments.find('.cb-comment-add-button').before(html);
760 $comments.find('.cb-comment-add-button').before(html);
761
761
762 //mark visually which comment was resolved
762 //mark visually which comment was resolved
763 if (resolvesCommentId) {
763 if (resolvesCommentId) {
764 commentForm.markCommentResolved(resolvesCommentId);
764 commentForm.markCommentResolved(resolvesCommentId);
765 }
765 }
766
766
767 // run global callback on submit
767 // run global callback on submit
768 commentForm.globalSubmitSuccessCallback();
768 commentForm.globalSubmitSuccessCallback();
769
769
770 } catch (e) {
770 } catch (e) {
771 console.error(e);
771 console.error(e);
772 }
772 }
773
773
774 // re trigger the linkification of next/prev navigation
774 // re trigger the linkification of next/prev navigation
775 linkifyComments($('.inline-comment-injected'));
775 linkifyComments($('.inline-comment-injected'));
776 timeagoActivate();
776 timeagoActivate();
777
777
778 if (window.Waypoint !== undefined) {
778 if (window.updateSticky !== undefined) {
779 // potentially our comments change the active window size, so we
779 // potentially our comments change the active window size, so we
780 // notify waypint to re-paint
780 // notify sticky elements
781 Waypoint.refreshAll()
781 updateSticky()
782 }
782 }
783
783
784 commentForm.setActionButtonsDisabled(false);
784 commentForm.setActionButtonsDisabled(false);
785
785
786 };
786 };
787 var submitFailCallback = function(data){
787 var submitFailCallback = function(data){
788 alert(
788 alert(
789 "Error while submitting comment.\n" +
789 "Error while submitting comment.\n" +
790 "Error code {0} ({1}).".format(data.status, data.statusText)
790 "Error code {0} ({1}).".format(data.status, data.statusText)
791 );
791 );
792 commentForm.resetCommentFormState(text)
792 commentForm.resetCommentFormState(text)
793 };
793 };
794 commentForm.submitAjaxPOST(
794 commentForm.submitAjaxPOST(
795 commentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback);
795 commentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback);
796 });
796 });
797 }
797 }
798
798
799 $form.addClass('comment-inline-form-open');
799 $form.addClass('comment-inline-form-open');
800 };
800 };
801
801
802 this.createResolutionComment = function(commentId){
802 this.createResolutionComment = function(commentId){
803 // hide the trigger text
803 // hide the trigger text
804 $('#resolve-comment-{0}'.format(commentId)).hide();
804 $('#resolve-comment-{0}'.format(commentId)).hide();
805
805
806 var comment = $('#comment-'+commentId);
806 var comment = $('#comment-'+commentId);
807 var commentData = comment.data();
807 var commentData = comment.data();
808 if (commentData.commentInline) {
808 if (commentData.commentInline) {
809 this.createComment(comment, commentId)
809 this.createComment(comment, commentId)
810 } else {
810 } else {
811 Rhodecode.comments.createGeneralComment('general', "$placeholder", commentId)
811 Rhodecode.comments.createGeneralComment('general', "$placeholder", commentId)
812 }
812 }
813
813
814 return false;
814 return false;
815 };
815 };
816
816
817 this.submitResolution = function(commentId){
817 this.submitResolution = function(commentId){
818 var form = $('#resolve_comment_{0}'.format(commentId)).closest('form');
818 var form = $('#resolve_comment_{0}'.format(commentId)).closest('form');
819 var commentForm = form.get(0).CommentForm;
819 var commentForm = form.get(0).CommentForm;
820
820
821 var cm = commentForm.getCmInstance();
821 var cm = commentForm.getCmInstance();
822 var renderer = templateContext.visual.default_renderer;
822 var renderer = templateContext.visual.default_renderer;
823 if (renderer == 'rst'){
823 if (renderer == 'rst'){
824 var commentUrl = '`#{0} <{1}#comment-{0}>`_'.format(commentId, commentForm.selfUrl);
824 var commentUrl = '`#{0} <{1}#comment-{0}>`_'.format(commentId, commentForm.selfUrl);
825 } else if (renderer == 'markdown') {
825 } else if (renderer == 'markdown') {
826 var commentUrl = '[#{0}]({1}#comment-{0})'.format(commentId, commentForm.selfUrl);
826 var commentUrl = '[#{0}]({1}#comment-{0})'.format(commentId, commentForm.selfUrl);
827 } else {
827 } else {
828 var commentUrl = '{1}#comment-{0}'.format(commentId, commentForm.selfUrl);
828 var commentUrl = '{1}#comment-{0}'.format(commentId, commentForm.selfUrl);
829 }
829 }
830
830
831 cm.setValue(_gettext('TODO from comment {0} was fixed.').format(commentUrl));
831 cm.setValue(_gettext('TODO from comment {0} was fixed.').format(commentUrl));
832 form.submit();
832 form.submit();
833 return false;
833 return false;
834 };
834 };
835
835
836 };
836 };
@@ -1,969 +1,970 b''
1 <%namespace name="commentblock" file="/changeset/changeset_file_comment.mako"/>
1 <%namespace name="commentblock" file="/changeset/changeset_file_comment.mako"/>
2
2
3 <%def name="diff_line_anchor(filename, line, type)"><%
3 <%def name="diff_line_anchor(filename, line, type)"><%
4 return '%s_%s_%i' % (h.safeid(filename), type, line)
4 return '%s_%s_%i' % (h.safeid(filename), type, line)
5 %></%def>
5 %></%def>
6
6
7 <%def name="action_class(action)">
7 <%def name="action_class(action)">
8 <%
8 <%
9 return {
9 return {
10 '-': 'cb-deletion',
10 '-': 'cb-deletion',
11 '+': 'cb-addition',
11 '+': 'cb-addition',
12 ' ': 'cb-context',
12 ' ': 'cb-context',
13 }.get(action, 'cb-empty')
13 }.get(action, 'cb-empty')
14 %>
14 %>
15 </%def>
15 </%def>
16
16
17 <%def name="op_class(op_id)">
17 <%def name="op_class(op_id)">
18 <%
18 <%
19 return {
19 return {
20 DEL_FILENODE: 'deletion', # file deleted
20 DEL_FILENODE: 'deletion', # file deleted
21 BIN_FILENODE: 'warning' # binary diff hidden
21 BIN_FILENODE: 'warning' # binary diff hidden
22 }.get(op_id, 'addition')
22 }.get(op_id, 'addition')
23 %>
23 %>
24 </%def>
24 </%def>
25
25
26
26
27
27
28 <%def name="render_diffset(diffset, commit=None,
28 <%def name="render_diffset(diffset, commit=None,
29
29
30 # collapse all file diff entries when there are more than this amount of files in the diff
30 # collapse all file diff entries when there are more than this amount of files in the diff
31 collapse_when_files_over=20,
31 collapse_when_files_over=20,
32
32
33 # collapse lines in the diff when more than this amount of lines changed in the file diff
33 # collapse lines in the diff when more than this amount of lines changed in the file diff
34 lines_changed_limit=500,
34 lines_changed_limit=500,
35
35
36 # add a ruler at to the output
36 # add a ruler at to the output
37 ruler_at_chars=0,
37 ruler_at_chars=0,
38
38
39 # show inline comments
39 # show inline comments
40 use_comments=False,
40 use_comments=False,
41
41
42 # disable new comments
42 # disable new comments
43 disable_new_comments=False,
43 disable_new_comments=False,
44
44
45 # special file-comments that were deleted in previous versions
45 # special file-comments that were deleted in previous versions
46 # it's used for showing outdated comments for deleted files in a PR
46 # it's used for showing outdated comments for deleted files in a PR
47 deleted_files_comments=None,
47 deleted_files_comments=None,
48
48
49 # for cache purpose
49 # for cache purpose
50 inline_comments=None,
50 inline_comments=None,
51
51
52 )">
52 )">
53 %if use_comments:
53 %if use_comments:
54 <div id="cb-comments-inline-container-template" class="js-template">
54 <div id="cb-comments-inline-container-template" class="js-template">
55 ${inline_comments_container([], inline_comments)}
55 ${inline_comments_container([], inline_comments)}
56 </div>
56 </div>
57 <div class="js-template" id="cb-comment-inline-form-template">
57 <div class="js-template" id="cb-comment-inline-form-template">
58 <div class="comment-inline-form ac">
58 <div class="comment-inline-form ac">
59
59
60 %if c.rhodecode_user.username != h.DEFAULT_USER:
60 %if c.rhodecode_user.username != h.DEFAULT_USER:
61 ## render template for inline comments
61 ## render template for inline comments
62 ${commentblock.comment_form(form_type='inline')}
62 ${commentblock.comment_form(form_type='inline')}
63 %else:
63 %else:
64 ${h.form('', class_='inline-form comment-form-login', method='get')}
64 ${h.form('', class_='inline-form comment-form-login', method='get')}
65 <div class="pull-left">
65 <div class="pull-left">
66 <div class="comment-help pull-right">
66 <div class="comment-help pull-right">
67 ${_('You need to be logged in to leave comments.')} <a href="${h.route_path('login', _query={'came_from': h.current_route_path(request)})}">${_('Login now')}</a>
67 ${_('You need to be logged in to leave comments.')} <a href="${h.route_path('login', _query={'came_from': h.current_route_path(request)})}">${_('Login now')}</a>
68 </div>
68 </div>
69 </div>
69 </div>
70 <div class="comment-button pull-right">
70 <div class="comment-button pull-right">
71 <button type="button" class="cb-comment-cancel" onclick="return Rhodecode.comments.cancelComment(this);">
71 <button type="button" class="cb-comment-cancel" onclick="return Rhodecode.comments.cancelComment(this);">
72 ${_('Cancel')}
72 ${_('Cancel')}
73 </button>
73 </button>
74 </div>
74 </div>
75 <div class="clearfix"></div>
75 <div class="clearfix"></div>
76 ${h.end_form()}
76 ${h.end_form()}
77 %endif
77 %endif
78 </div>
78 </div>
79 </div>
79 </div>
80
80
81 %endif
81 %endif
82 <%
82 <%
83 collapse_all = len(diffset.files) > collapse_when_files_over
83 collapse_all = len(diffset.files) > collapse_when_files_over
84 %>
84 %>
85
85
86 %if c.user_session_attrs["diffmode"] == 'sideside':
86 %if c.user_session_attrs["diffmode"] == 'sideside':
87 <style>
87 <style>
88 .wrapper {
88 .wrapper {
89 max-width: 1600px !important;
89 max-width: 1600px !important;
90 }
90 }
91 </style>
91 </style>
92 %endif
92 %endif
93
93
94 %if ruler_at_chars:
94 %if ruler_at_chars:
95 <style>
95 <style>
96 .diff table.cb .cb-content:after {
96 .diff table.cb .cb-content:after {
97 content: "";
97 content: "";
98 border-left: 1px solid blue;
98 border-left: 1px solid blue;
99 position: absolute;
99 position: absolute;
100 top: 0;
100 top: 0;
101 height: 18px;
101 height: 18px;
102 opacity: .2;
102 opacity: .2;
103 z-index: 10;
103 z-index: 10;
104 //## +5 to account for diff action (+/-)
104 //## +5 to account for diff action (+/-)
105 left: ${ruler_at_chars + 5}ch;
105 left: ${ruler_at_chars + 5}ch;
106 </style>
106 </style>
107 %endif
107 %endif
108
108
109 <div class="diffset ${disable_new_comments and 'diffset-comments-disabled'}">
109 <div class="diffset ${disable_new_comments and 'diffset-comments-disabled'}">
110 <div class="diffset-heading ${diffset.limited_diff and 'diffset-heading-warning' or ''}">
110 <div class="diffset-heading ${diffset.limited_diff and 'diffset-heading-warning' or ''}">
111 %if commit:
111 %if commit:
112 <div class="pull-right">
112 <div class="pull-right">
113 <a class="btn tooltip" title="${h.tooltip(_('Browse Files at revision {}').format(commit.raw_id))}" href="${h.route_path('repo_files',repo_name=diffset.repo_name, commit_id=commit.raw_id, f_path='')}">
113 <a class="btn tooltip" title="${h.tooltip(_('Browse Files at revision {}').format(commit.raw_id))}" href="${h.route_path('repo_files',repo_name=diffset.repo_name, commit_id=commit.raw_id, f_path='')}">
114 ${_('Browse Files')}
114 ${_('Browse Files')}
115 </a>
115 </a>
116 </div>
116 </div>
117 %endif
117 %endif
118 <h2 class="clearinner">
118 <h2 class="clearinner">
119 ## invidual commit
119 ## invidual commit
120 % if commit:
120 % if commit:
121 <a class="tooltip revision" title="${h.tooltip(commit.message)}" href="${h.route_path('repo_commit',repo_name=diffset.repo_name,commit_id=commit.raw_id)}">${('r%s:%s' % (commit.idx,h.short_id(commit.raw_id)))}</a> -
121 <a class="tooltip revision" title="${h.tooltip(commit.message)}" href="${h.route_path('repo_commit',repo_name=diffset.repo_name,commit_id=commit.raw_id)}">${('r%s:%s' % (commit.idx,h.short_id(commit.raw_id)))}</a> -
122 ${h.age_component(commit.date)}
122 ${h.age_component(commit.date)}
123 % if diffset.limited_diff:
123 % if diffset.limited_diff:
124 - ${_('The requested commit is too big and content was truncated.')}
124 - ${_('The requested commit is too big and content was truncated.')}
125 ${_ungettext('%(num)s file changed.', '%(num)s files changed.', diffset.changed_files) % {'num': diffset.changed_files}}
125 ${_ungettext('%(num)s file changed.', '%(num)s files changed.', diffset.changed_files) % {'num': diffset.changed_files}}
126 <a href="${h.current_route_path(request, fulldiff=1)}" onclick="return confirm('${_("Showing a big diff might take some time and resources, continue?")}')">${_('Show full diff')}</a>
126 <a href="${h.current_route_path(request, fulldiff=1)}" onclick="return confirm('${_("Showing a big diff might take some time and resources, continue?")}')">${_('Show full diff')}</a>
127 % elif hasattr(c, 'commit_ranges') and len(c.commit_ranges) > 1:
127 % elif hasattr(c, 'commit_ranges') and len(c.commit_ranges) > 1:
128 ## compare diff, has no file-selector and we want to show stats anyway
128 ## compare diff, has no file-selector and we want to show stats anyway
129 ${_ungettext('{num} file changed: {linesadd} inserted, ''{linesdel} deleted',
129 ${_ungettext('{num} file changed: {linesadd} inserted, ''{linesdel} deleted',
130 '{num} files changed: {linesadd} inserted, {linesdel} deleted', diffset.changed_files) \
130 '{num} files changed: {linesadd} inserted, {linesdel} deleted', diffset.changed_files) \
131 .format(num=diffset.changed_files, linesadd=diffset.lines_added, linesdel=diffset.lines_deleted)}
131 .format(num=diffset.changed_files, linesadd=diffset.lines_added, linesdel=diffset.lines_deleted)}
132 % endif
132 % endif
133 % else:
133 % else:
134 ## pull requests/compare
134 ## pull requests/compare
135 ${_('File Changes')}
135 ${_('File Changes')}
136 % endif
136 % endif
137
137
138 </h2>
138 </h2>
139 </div>
139 </div>
140
140
141 %if diffset.has_hidden_changes:
141 %if diffset.has_hidden_changes:
142 <p class="empty_data">${_('Some changes may be hidden')}</p>
142 <p class="empty_data">${_('Some changes may be hidden')}</p>
143 %elif not diffset.files:
143 %elif not diffset.files:
144 <p class="empty_data">${_('No files')}</p>
144 <p class="empty_data">${_('No files')}</p>
145 %endif
145 %endif
146
146
147 <div class="filediffs">
147 <div class="filediffs">
148
148
149 ## initial value could be marked as False later on
149 ## initial value could be marked as False later on
150 <% over_lines_changed_limit = False %>
150 <% over_lines_changed_limit = False %>
151 %for i, filediff in enumerate(diffset.files):
151 %for i, filediff in enumerate(diffset.files):
152
152
153 <%
153 <%
154 lines_changed = filediff.patch['stats']['added'] + filediff.patch['stats']['deleted']
154 lines_changed = filediff.patch['stats']['added'] + filediff.patch['stats']['deleted']
155 over_lines_changed_limit = lines_changed > lines_changed_limit
155 over_lines_changed_limit = lines_changed > lines_changed_limit
156 %>
156 %>
157 ## anchor with support of sticky header
157 ## anchor with support of sticky header
158 <div class="anchor" id="a_${h.FID(filediff.raw_id, filediff.patch['filename'])}"></div>
158 <div class="anchor" id="a_${h.FID(filediff.raw_id, filediff.patch['filename'])}"></div>
159
159
160 <input ${(collapse_all and 'checked' or '')} class="filediff-collapse-state" id="filediff-collapse-${id(filediff)}" type="checkbox" onchange="Waypoint.refreshAll();">
160 <input ${(collapse_all and 'checked' or '')} class="filediff-collapse-state" id="filediff-collapse-${id(filediff)}" type="checkbox" onchange="updateSticky();">
161 <div
161 <div
162 class="filediff"
162 class="filediff"
163 data-f-path="${filediff.patch['filename']}"
163 data-f-path="${filediff.patch['filename']}"
164 data-anchor-id="${h.FID(filediff.raw_id, filediff.patch['filename'])}"
164 data-anchor-id="${h.FID(filediff.raw_id, filediff.patch['filename'])}"
165 >
165 >
166 <label for="filediff-collapse-${id(filediff)}" class="filediff-heading">
166 <label for="filediff-collapse-${id(filediff)}" class="filediff-heading">
167 <div class="filediff-collapse-indicator"></div>
167 <div class="filediff-collapse-indicator"></div>
168 ${diff_ops(filediff)}
168 ${diff_ops(filediff)}
169 </label>
169 </label>
170
170
171 ${diff_menu(filediff, use_comments=use_comments)}
171 ${diff_menu(filediff, use_comments=use_comments)}
172 <table data-f-path="${filediff.patch['filename']}" data-anchor-id="${h.FID(filediff.raw_id, filediff.patch['filename'])}" class="code-visible-block cb cb-diff-${c.user_session_attrs["diffmode"]} code-highlight ${(over_lines_changed_limit and 'cb-collapsed' or '')}">
172 <table data-f-path="${filediff.patch['filename']}" data-anchor-id="${h.FID(filediff.raw_id, filediff.patch['filename'])}" class="code-visible-block cb cb-diff-${c.user_session_attrs["diffmode"]} code-highlight ${(over_lines_changed_limit and 'cb-collapsed' or '')}">
173
173
174 ## new/deleted/empty content case
174 ## new/deleted/empty content case
175 % if not filediff.hunks:
175 % if not filediff.hunks:
176 ## Comment container, on "fakes" hunk that contains all data to render comments
176 ## Comment container, on "fakes" hunk that contains all data to render comments
177 ${render_hunk_lines(c.user_session_attrs["diffmode"], filediff.hunk_ops, use_comments=use_comments, inline_comments=inline_comments)}
177 ${render_hunk_lines(c.user_session_attrs["diffmode"], filediff.hunk_ops, use_comments=use_comments, inline_comments=inline_comments)}
178 % endif
178 % endif
179
179
180 %if filediff.limited_diff:
180 %if filediff.limited_diff:
181 <tr class="cb-warning cb-collapser">
181 <tr class="cb-warning cb-collapser">
182 <td class="cb-text" ${(c.user_session_attrs["diffmode"] == 'unified' and 'colspan=4' or 'colspan=6')}>
182 <td class="cb-text" ${(c.user_session_attrs["diffmode"] == 'unified' and 'colspan=4' or 'colspan=6')}>
183 ${_('The requested commit is too big and content was truncated.')} <a href="${h.current_route_path(request, fulldiff=1)}" onclick="return confirm('${_("Showing a big diff might take some time and resources, continue?")}')">${_('Show full diff')}</a>
183 ${_('The requested commit is too big and content was truncated.')} <a href="${h.current_route_path(request, fulldiff=1)}" onclick="return confirm('${_("Showing a big diff might take some time and resources, continue?")}')">${_('Show full diff')}</a>
184 </td>
184 </td>
185 </tr>
185 </tr>
186 %else:
186 %else:
187 %if over_lines_changed_limit:
187 %if over_lines_changed_limit:
188 <tr class="cb-warning cb-collapser">
188 <tr class="cb-warning cb-collapser">
189 <td class="cb-text" ${(c.user_session_attrs["diffmode"] == 'unified' and 'colspan=4' or 'colspan=6')}>
189 <td class="cb-text" ${(c.user_session_attrs["diffmode"] == 'unified' and 'colspan=4' or 'colspan=6')}>
190 ${_('This diff has been collapsed as it changes many lines, (%i lines changed)' % lines_changed)}
190 ${_('This diff has been collapsed as it changes many lines, (%i lines changed)' % lines_changed)}
191 <a href="#" class="cb-expand"
191 <a href="#" class="cb-expand"
192 onclick="$(this).closest('table').removeClass('cb-collapsed'); return false;">${_('Show them')}
192 onclick="$(this).closest('table').removeClass('cb-collapsed'); updateSticky(); return false;">${_('Show them')}
193 </a>
193 </a>
194 <a href="#" class="cb-collapse"
194 <a href="#" class="cb-collapse"
195 onclick="$(this).closest('table').addClass('cb-collapsed'); return false;">${_('Hide them')}
195 onclick="$(this).closest('table').addClass('cb-collapsed'); updateSticky(); return false;">${_('Hide them')}
196 </a>
196 </a>
197 </td>
197 </td>
198 </tr>
198 </tr>
199 %endif
199 %endif
200 %endif
200 %endif
201
201
202 % for hunk in filediff.hunks:
202 % for hunk in filediff.hunks:
203 <tr class="cb-hunk">
203 <tr class="cb-hunk">
204 <td ${(c.user_session_attrs["diffmode"] == 'unified' and 'colspan=3' or '')}>
204 <td ${(c.user_session_attrs["diffmode"] == 'unified' and 'colspan=3' or '')}>
205 ## TODO: dan: add ajax loading of more context here
205 ## TODO: dan: add ajax loading of more context here
206 ## <a href="#">
206 ## <a href="#">
207 <i class="icon-more"></i>
207 <i class="icon-more"></i>
208 ## </a>
208 ## </a>
209 </td>
209 </td>
210 <td ${(c.user_session_attrs["diffmode"] == 'sideside' and 'colspan=5' or '')}>
210 <td ${(c.user_session_attrs["diffmode"] == 'sideside' and 'colspan=5' or '')}>
211 @@
211 @@
212 -${hunk.source_start},${hunk.source_length}
212 -${hunk.source_start},${hunk.source_length}
213 +${hunk.target_start},${hunk.target_length}
213 +${hunk.target_start},${hunk.target_length}
214 ${hunk.section_header}
214 ${hunk.section_header}
215 </td>
215 </td>
216 </tr>
216 </tr>
217 ${render_hunk_lines(c.user_session_attrs["diffmode"], hunk, use_comments=use_comments, inline_comments=inline_comments)}
217 ${render_hunk_lines(c.user_session_attrs["diffmode"], hunk, use_comments=use_comments, inline_comments=inline_comments)}
218 % endfor
218 % endfor
219
219
220 <% unmatched_comments = (inline_comments or {}).get(filediff.patch['filename'], {}) %>
220 <% unmatched_comments = (inline_comments or {}).get(filediff.patch['filename'], {}) %>
221
221
222 ## outdated comments that do not fit into currently displayed lines
222 ## outdated comments that do not fit into currently displayed lines
223 % for lineno, comments in unmatched_comments.items():
223 % for lineno, comments in unmatched_comments.items():
224
224
225 %if c.user_session_attrs["diffmode"] == 'unified':
225 %if c.user_session_attrs["diffmode"] == 'unified':
226 % if loop.index == 0:
226 % if loop.index == 0:
227 <tr class="cb-hunk">
227 <tr class="cb-hunk">
228 <td colspan="3"></td>
228 <td colspan="3"></td>
229 <td>
229 <td>
230 <div>
230 <div>
231 ${_('Unmatched inline comments below')}
231 ${_('Unmatched inline comments below')}
232 </div>
232 </div>
233 </td>
233 </td>
234 </tr>
234 </tr>
235 % endif
235 % endif
236 <tr class="cb-line">
236 <tr class="cb-line">
237 <td class="cb-data cb-context"></td>
237 <td class="cb-data cb-context"></td>
238 <td class="cb-lineno cb-context"></td>
238 <td class="cb-lineno cb-context"></td>
239 <td class="cb-lineno cb-context"></td>
239 <td class="cb-lineno cb-context"></td>
240 <td class="cb-content cb-context">
240 <td class="cb-content cb-context">
241 ${inline_comments_container(comments, inline_comments)}
241 ${inline_comments_container(comments, inline_comments)}
242 </td>
242 </td>
243 </tr>
243 </tr>
244 %elif c.user_session_attrs["diffmode"] == 'sideside':
244 %elif c.user_session_attrs["diffmode"] == 'sideside':
245 % if loop.index == 0:
245 % if loop.index == 0:
246 <tr class="cb-comment-info">
246 <tr class="cb-comment-info">
247 <td colspan="2"></td>
247 <td colspan="2"></td>
248 <td class="cb-line">
248 <td class="cb-line">
249 <div>
249 <div>
250 ${_('Unmatched inline comments below')}
250 ${_('Unmatched inline comments below')}
251 </div>
251 </div>
252 </td>
252 </td>
253 <td colspan="2"></td>
253 <td colspan="2"></td>
254 <td class="cb-line">
254 <td class="cb-line">
255 <div>
255 <div>
256 ${_('Unmatched comments below')}
256 ${_('Unmatched comments below')}
257 </div>
257 </div>
258 </td>
258 </td>
259 </tr>
259 </tr>
260 % endif
260 % endif
261 <tr class="cb-line">
261 <tr class="cb-line">
262 <td class="cb-data cb-context"></td>
262 <td class="cb-data cb-context"></td>
263 <td class="cb-lineno cb-context"></td>
263 <td class="cb-lineno cb-context"></td>
264 <td class="cb-content cb-context">
264 <td class="cb-content cb-context">
265 % if lineno.startswith('o'):
265 % if lineno.startswith('o'):
266 ${inline_comments_container(comments, inline_comments)}
266 ${inline_comments_container(comments, inline_comments)}
267 % endif
267 % endif
268 </td>
268 </td>
269
269
270 <td class="cb-data cb-context"></td>
270 <td class="cb-data cb-context"></td>
271 <td class="cb-lineno cb-context"></td>
271 <td class="cb-lineno cb-context"></td>
272 <td class="cb-content cb-context">
272 <td class="cb-content cb-context">
273 % if lineno.startswith('n'):
273 % if lineno.startswith('n'):
274 ${inline_comments_container(comments, inline_comments)}
274 ${inline_comments_container(comments, inline_comments)}
275 % endif
275 % endif
276 </td>
276 </td>
277 </tr>
277 </tr>
278 %endif
278 %endif
279
279
280 % endfor
280 % endfor
281
281
282 </table>
282 </table>
283 </div>
283 </div>
284 %endfor
284 %endfor
285
285
286 ## outdated comments that are made for a file that has been deleted
286 ## outdated comments that are made for a file that has been deleted
287 % for filename, comments_dict in (deleted_files_comments or {}).items():
287 % for filename, comments_dict in (deleted_files_comments or {}).items():
288 <%
288 <%
289 display_state = 'display: none'
289 display_state = 'display: none'
290 open_comments_in_file = [x for x in comments_dict['comments'] if x.outdated is False]
290 open_comments_in_file = [x for x in comments_dict['comments'] if x.outdated is False]
291 if open_comments_in_file:
291 if open_comments_in_file:
292 display_state = ''
292 display_state = ''
293 %>
293 %>
294 <div class="filediffs filediff-outdated" style="${display_state}">
294 <div class="filediffs filediff-outdated" style="${display_state}">
295 <input ${(collapse_all and 'checked' or '')} class="filediff-collapse-state" id="filediff-collapse-${id(filename)}" type="checkbox" onchange="Waypoint.refreshAll();">
295 <input ${(collapse_all and 'checked' or '')} class="filediff-collapse-state" id="filediff-collapse-${id(filename)}" type="checkbox" onchange="updateSticky();">
296 <div class="filediff" data-f-path="${filename}" id="a_${h.FID(filediff.raw_id, filename)}">
296 <div class="filediff" data-f-path="${filename}" id="a_${h.FID(filediff.raw_id, filename)}">
297 <label for="filediff-collapse-${id(filename)}" class="filediff-heading">
297 <label for="filediff-collapse-${id(filename)}" class="filediff-heading">
298 <div class="filediff-collapse-indicator"></div>
298 <div class="filediff-collapse-indicator"></div>
299 <span class="pill">
299 <span class="pill">
300 ## file was deleted
300 ## file was deleted
301 <strong>${filename}</strong>
301 <strong>${filename}</strong>
302 </span>
302 </span>
303 <span class="pill-group" style="float: left">
303 <span class="pill-group" style="float: left">
304 ## file op, doesn't need translation
304 ## file op, doesn't need translation
305 <span class="pill" op="removed">removed in this version</span>
305 <span class="pill" op="removed">removed in this version</span>
306 </span>
306 </span>
307 <a class="pill filediff-anchor" href="#a_${h.FID(filediff.raw_id, filename)}">ΒΆ</a>
307 <a class="pill filediff-anchor" href="#a_${h.FID(filediff.raw_id, filename)}">ΒΆ</a>
308 <span class="pill-group" style="float: right">
308 <span class="pill-group" style="float: right">
309 <span class="pill" op="deleted">-${comments_dict['stats']}</span>
309 <span class="pill" op="deleted">-${comments_dict['stats']}</span>
310 </span>
310 </span>
311 </label>
311 </label>
312
312
313 <table class="cb cb-diff-${c.user_session_attrs["diffmode"]} code-highlight ${over_lines_changed_limit and 'cb-collapsed' or ''}">
313 <table class="cb cb-diff-${c.user_session_attrs["diffmode"]} code-highlight ${over_lines_changed_limit and 'cb-collapsed' or ''}">
314 <tr>
314 <tr>
315 % if c.user_session_attrs["diffmode"] == 'unified':
315 % if c.user_session_attrs["diffmode"] == 'unified':
316 <td></td>
316 <td></td>
317 %endif
317 %endif
318
318
319 <td></td>
319 <td></td>
320 <td class="cb-text cb-${op_class(BIN_FILENODE)}" ${(c.user_session_attrs["diffmode"] == 'unified' and 'colspan=4' or 'colspan=5')}>
320 <td class="cb-text cb-${op_class(BIN_FILENODE)}" ${(c.user_session_attrs["diffmode"] == 'unified' and 'colspan=4' or 'colspan=5')}>
321 ${_('File was deleted in this version. There are still outdated/unresolved comments attached to it.')}
321 ${_('File was deleted in this version. There are still outdated/unresolved comments attached to it.')}
322 </td>
322 </td>
323 </tr>
323 </tr>
324 %if c.user_session_attrs["diffmode"] == 'unified':
324 %if c.user_session_attrs["diffmode"] == 'unified':
325 <tr class="cb-line">
325 <tr class="cb-line">
326 <td class="cb-data cb-context"></td>
326 <td class="cb-data cb-context"></td>
327 <td class="cb-lineno cb-context"></td>
327 <td class="cb-lineno cb-context"></td>
328 <td class="cb-lineno cb-context"></td>
328 <td class="cb-lineno cb-context"></td>
329 <td class="cb-content cb-context">
329 <td class="cb-content cb-context">
330 ${inline_comments_container(comments_dict['comments'], inline_comments)}
330 ${inline_comments_container(comments_dict['comments'], inline_comments)}
331 </td>
331 </td>
332 </tr>
332 </tr>
333 %elif c.user_session_attrs["diffmode"] == 'sideside':
333 %elif c.user_session_attrs["diffmode"] == 'sideside':
334 <tr class="cb-line">
334 <tr class="cb-line">
335 <td class="cb-data cb-context"></td>
335 <td class="cb-data cb-context"></td>
336 <td class="cb-lineno cb-context"></td>
336 <td class="cb-lineno cb-context"></td>
337 <td class="cb-content cb-context"></td>
337 <td class="cb-content cb-context"></td>
338
338
339 <td class="cb-data cb-context"></td>
339 <td class="cb-data cb-context"></td>
340 <td class="cb-lineno cb-context"></td>
340 <td class="cb-lineno cb-context"></td>
341 <td class="cb-content cb-context">
341 <td class="cb-content cb-context">
342 ${inline_comments_container(comments_dict['comments'], inline_comments)}
342 ${inline_comments_container(comments_dict['comments'], inline_comments)}
343 </td>
343 </td>
344 </tr>
344 </tr>
345 %endif
345 %endif
346 </table>
346 </table>
347 </div>
347 </div>
348 </div>
348 </div>
349 % endfor
349 % endfor
350
350
351 </div>
351 </div>
352 </div>
352 </div>
353 </%def>
353 </%def>
354
354
355 <%def name="diff_ops(filediff)">
355 <%def name="diff_ops(filediff)">
356 <%
356 <%
357 from rhodecode.lib.diffs import NEW_FILENODE, DEL_FILENODE, \
357 from rhodecode.lib.diffs import NEW_FILENODE, DEL_FILENODE, \
358 MOD_FILENODE, RENAMED_FILENODE, CHMOD_FILENODE, BIN_FILENODE, COPIED_FILENODE
358 MOD_FILENODE, RENAMED_FILENODE, CHMOD_FILENODE, BIN_FILENODE, COPIED_FILENODE
359 %>
359 %>
360 <span class="pill">
360 <span class="pill">
361 %if filediff.source_file_path and filediff.target_file_path:
361 %if filediff.source_file_path and filediff.target_file_path:
362 %if filediff.source_file_path != filediff.target_file_path:
362 %if filediff.source_file_path != filediff.target_file_path:
363 ## file was renamed, or copied
363 ## file was renamed, or copied
364 %if RENAMED_FILENODE in filediff.patch['stats']['ops']:
364 %if RENAMED_FILENODE in filediff.patch['stats']['ops']:
365 <strong>${filediff.target_file_path}</strong> β¬… <del>${filediff.source_file_path}</del>
365 <strong>${filediff.target_file_path}</strong> β¬… <del>${filediff.source_file_path}</del>
366 <% final_path = filediff.target_file_path %>
366 <% final_path = filediff.target_file_path %>
367 %elif COPIED_FILENODE in filediff.patch['stats']['ops']:
367 %elif COPIED_FILENODE in filediff.patch['stats']['ops']:
368 <strong>${filediff.target_file_path}</strong> β¬… ${filediff.source_file_path}
368 <strong>${filediff.target_file_path}</strong> β¬… ${filediff.source_file_path}
369 <% final_path = filediff.target_file_path %>
369 <% final_path = filediff.target_file_path %>
370 %endif
370 %endif
371 %else:
371 %else:
372 ## file was modified
372 ## file was modified
373 <strong>${filediff.source_file_path}</strong>
373 <strong>${filediff.source_file_path}</strong>
374 <% final_path = filediff.source_file_path %>
374 <% final_path = filediff.source_file_path %>
375 %endif
375 %endif
376 %else:
376 %else:
377 %if filediff.source_file_path:
377 %if filediff.source_file_path:
378 ## file was deleted
378 ## file was deleted
379 <strong>${filediff.source_file_path}</strong>
379 <strong>${filediff.source_file_path}</strong>
380 <% final_path = filediff.source_file_path %>
380 <% final_path = filediff.source_file_path %>
381 %else:
381 %else:
382 ## file was added
382 ## file was added
383 <strong>${filediff.target_file_path}</strong>
383 <strong>${filediff.target_file_path}</strong>
384 <% final_path = filediff.target_file_path %>
384 <% final_path = filediff.target_file_path %>
385 %endif
385 %endif
386 %endif
386 %endif
387 <i style="color: #aaa" class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${final_path}" title="${_('Copy the full path')}" onclick="return false;"></i>
387 <i style="color: #aaa" class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${final_path}" title="${_('Copy the full path')}" onclick="return false;"></i>
388 </span>
388 </span>
389 ## anchor link
389 ## anchor link
390 <a class="pill filediff-anchor" href="#a_${h.FID(filediff.raw_id, filediff.patch['filename'])}">ΒΆ</a>
390 <a class="pill filediff-anchor" href="#a_${h.FID(filediff.raw_id, filediff.patch['filename'])}">ΒΆ</a>
391
391
392 <span class="pill-group" style="float: right">
392 <span class="pill-group" style="float: right">
393
393
394 ## ops pills
394 ## ops pills
395 %if filediff.limited_diff:
395 %if filediff.limited_diff:
396 <span class="pill tooltip" op="limited" title="The stats for this diff are not complete">limited diff</span>
396 <span class="pill tooltip" op="limited" title="The stats for this diff are not complete">limited diff</span>
397 %endif
397 %endif
398
398
399 %if NEW_FILENODE in filediff.patch['stats']['ops']:
399 %if NEW_FILENODE in filediff.patch['stats']['ops']:
400 <span class="pill" op="created">created</span>
400 <span class="pill" op="created">created</span>
401 %if filediff['target_mode'].startswith('120'):
401 %if filediff['target_mode'].startswith('120'):
402 <span class="pill" op="symlink">symlink</span>
402 <span class="pill" op="symlink">symlink</span>
403 %else:
403 %else:
404 <span class="pill" op="mode">${nice_mode(filediff['target_mode'])}</span>
404 <span class="pill" op="mode">${nice_mode(filediff['target_mode'])}</span>
405 %endif
405 %endif
406 %endif
406 %endif
407
407
408 %if RENAMED_FILENODE in filediff.patch['stats']['ops']:
408 %if RENAMED_FILENODE in filediff.patch['stats']['ops']:
409 <span class="pill" op="renamed">renamed</span>
409 <span class="pill" op="renamed">renamed</span>
410 %endif
410 %endif
411
411
412 %if COPIED_FILENODE in filediff.patch['stats']['ops']:
412 %if COPIED_FILENODE in filediff.patch['stats']['ops']:
413 <span class="pill" op="copied">copied</span>
413 <span class="pill" op="copied">copied</span>
414 %endif
414 %endif
415
415
416 %if DEL_FILENODE in filediff.patch['stats']['ops']:
416 %if DEL_FILENODE in filediff.patch['stats']['ops']:
417 <span class="pill" op="removed">removed</span>
417 <span class="pill" op="removed">removed</span>
418 %endif
418 %endif
419
419
420 %if CHMOD_FILENODE in filediff.patch['stats']['ops']:
420 %if CHMOD_FILENODE in filediff.patch['stats']['ops']:
421 <span class="pill" op="mode">
421 <span class="pill" op="mode">
422 ${nice_mode(filediff['source_mode'])} ➑ ${nice_mode(filediff['target_mode'])}
422 ${nice_mode(filediff['source_mode'])} ➑ ${nice_mode(filediff['target_mode'])}
423 </span>
423 </span>
424 %endif
424 %endif
425
425
426 %if BIN_FILENODE in filediff.patch['stats']['ops']:
426 %if BIN_FILENODE in filediff.patch['stats']['ops']:
427 <span class="pill" op="binary">binary</span>
427 <span class="pill" op="binary">binary</span>
428 %if MOD_FILENODE in filediff.patch['stats']['ops']:
428 %if MOD_FILENODE in filediff.patch['stats']['ops']:
429 <span class="pill" op="modified">modified</span>
429 <span class="pill" op="modified">modified</span>
430 %endif
430 %endif
431 %endif
431 %endif
432
432
433 <span class="pill" op="added">${('+' if filediff.patch['stats']['added'] else '')}${filediff.patch['stats']['added']}</span>
433 <span class="pill" op="added">${('+' if filediff.patch['stats']['added'] else '')}${filediff.patch['stats']['added']}</span>
434 <span class="pill" op="deleted">${((h.safe_int(filediff.patch['stats']['deleted']) or 0) * -1)}</span>
434 <span class="pill" op="deleted">${((h.safe_int(filediff.patch['stats']['deleted']) or 0) * -1)}</span>
435
435
436 </span>
436 </span>
437
437
438 </%def>
438 </%def>
439
439
440 <%def name="nice_mode(filemode)">
440 <%def name="nice_mode(filemode)">
441 ${(filemode.startswith('100') and filemode[3:] or filemode)}
441 ${(filemode.startswith('100') and filemode[3:] or filemode)}
442 </%def>
442 </%def>
443
443
444 <%def name="diff_menu(filediff, use_comments=False)">
444 <%def name="diff_menu(filediff, use_comments=False)">
445 <div class="filediff-menu">
445 <div class="filediff-menu">
446 %if filediff.diffset.source_ref:
446 %if filediff.diffset.source_ref:
447 %if filediff.operation in ['D', 'M']:
447 %if filediff.operation in ['D', 'M']:
448 <a
448 <a
449 class="tooltip"
449 class="tooltip"
450 href="${h.route_path('repo_files',repo_name=filediff.diffset.repo_name,commit_id=filediff.diffset.source_ref,f_path=filediff.source_file_path)}"
450 href="${h.route_path('repo_files',repo_name=filediff.diffset.repo_name,commit_id=filediff.diffset.source_ref,f_path=filediff.source_file_path)}"
451 title="${h.tooltip(_('Show file at commit: %(commit_id)s') % {'commit_id': filediff.diffset.source_ref[:12]})}"
451 title="${h.tooltip(_('Show file at commit: %(commit_id)s') % {'commit_id': filediff.diffset.source_ref[:12]})}"
452 >
452 >
453 ${_('Show file before')}
453 ${_('Show file before')}
454 </a> |
454 </a> |
455 %else:
455 %else:
456 <span
456 <span
457 class="tooltip"
457 class="tooltip"
458 title="${h.tooltip(_('File no longer present at commit: %(commit_id)s') % {'commit_id': filediff.diffset.source_ref[:12]})}"
458 title="${h.tooltip(_('File no longer present at commit: %(commit_id)s') % {'commit_id': filediff.diffset.source_ref[:12]})}"
459 >
459 >
460 ${_('Show file before')}
460 ${_('Show file before')}
461 </span> |
461 </span> |
462 %endif
462 %endif
463 %if filediff.operation in ['A', 'M']:
463 %if filediff.operation in ['A', 'M']:
464 <a
464 <a
465 class="tooltip"
465 class="tooltip"
466 href="${h.route_path('repo_files',repo_name=filediff.diffset.source_repo_name,commit_id=filediff.diffset.target_ref,f_path=filediff.target_file_path)}"
466 href="${h.route_path('repo_files',repo_name=filediff.diffset.source_repo_name,commit_id=filediff.diffset.target_ref,f_path=filediff.target_file_path)}"
467 title="${h.tooltip(_('Show file at commit: %(commit_id)s') % {'commit_id': filediff.diffset.target_ref[:12]})}"
467 title="${h.tooltip(_('Show file at commit: %(commit_id)s') % {'commit_id': filediff.diffset.target_ref[:12]})}"
468 >
468 >
469 ${_('Show file after')}
469 ${_('Show file after')}
470 </a> |
470 </a> |
471 %else:
471 %else:
472 <span
472 <span
473 class="tooltip"
473 class="tooltip"
474 title="${h.tooltip(_('File no longer present at commit: %(commit_id)s') % {'commit_id': filediff.diffset.target_ref[:12]})}"
474 title="${h.tooltip(_('File no longer present at commit: %(commit_id)s') % {'commit_id': filediff.diffset.target_ref[:12]})}"
475 >
475 >
476 ${_('Show file after')}
476 ${_('Show file after')}
477 </span> |
477 </span> |
478 %endif
478 %endif
479 <a
479 <a
480 class="tooltip"
480 class="tooltip"
481 title="${h.tooltip(_('Raw diff'))}"
481 title="${h.tooltip(_('Raw diff'))}"
482 href="${h.route_path('repo_files_diff',repo_name=filediff.diffset.repo_name,f_path=filediff.target_file_path, _query=dict(diff2=filediff.diffset.target_ref,diff1=filediff.diffset.source_ref,diff='raw'))}"
482 href="${h.route_path('repo_files_diff',repo_name=filediff.diffset.repo_name,f_path=filediff.target_file_path, _query=dict(diff2=filediff.diffset.target_ref,diff1=filediff.diffset.source_ref,diff='raw'))}"
483 >
483 >
484 ${_('Raw diff')}
484 ${_('Raw diff')}
485 </a> |
485 </a> |
486 <a
486 <a
487 class="tooltip"
487 class="tooltip"
488 title="${h.tooltip(_('Download diff'))}"
488 title="${h.tooltip(_('Download diff'))}"
489 href="${h.route_path('repo_files_diff',repo_name=filediff.diffset.repo_name,f_path=filediff.target_file_path, _query=dict(diff2=filediff.diffset.target_ref,diff1=filediff.diffset.source_ref,diff='download'))}"
489 href="${h.route_path('repo_files_diff',repo_name=filediff.diffset.repo_name,f_path=filediff.target_file_path, _query=dict(diff2=filediff.diffset.target_ref,diff1=filediff.diffset.source_ref,diff='download'))}"
490 >
490 >
491 ${_('Download diff')}
491 ${_('Download diff')}
492 </a>
492 </a>
493 % if use_comments:
493 % if use_comments:
494 |
494 |
495 % endif
495 % endif
496
496
497 ## TODO: dan: refactor ignorews_url and context_url into the diff renderer same as diffmode=unified/sideside. Also use ajax to load more context (by clicking hunks)
497 ## TODO: dan: refactor ignorews_url and context_url into the diff renderer same as diffmode=unified/sideside. Also use ajax to load more context (by clicking hunks)
498 %if hasattr(c, 'ignorews_url'):
498 %if hasattr(c, 'ignorews_url'):
499 ${c.ignorews_url(request, h.FID(filediff.raw_id, filediff.patch['filename']))}
499 ${c.ignorews_url(request, h.FID(filediff.raw_id, filediff.patch['filename']))}
500 %endif
500 %endif
501 %if hasattr(c, 'context_url'):
501 %if hasattr(c, 'context_url'):
502 ${c.context_url(request, h.FID(filediff.raw_id, filediff.patch['filename']))}
502 ${c.context_url(request, h.FID(filediff.raw_id, filediff.patch['filename']))}
503 %endif
503 %endif
504
504
505 %if use_comments:
505 %if use_comments:
506 <a href="#" onclick="return Rhodecode.comments.toggleComments(this);">
506 <a href="#" onclick="return Rhodecode.comments.toggleComments(this);">
507 <span class="show-comment-button">${_('Show comments')}</span><span class="hide-comment-button">${_('Hide comments')}</span>
507 <span class="show-comment-button">${_('Show comments')}</span><span class="hide-comment-button">${_('Hide comments')}</span>
508 </a>
508 </a>
509 %endif
509 %endif
510 %endif
510 %endif
511 </div>
511 </div>
512 </%def>
512 </%def>
513
513
514
514
515 <%def name="inline_comments_container(comments, inline_comments)">
515 <%def name="inline_comments_container(comments, inline_comments)">
516 <div class="inline-comments">
516 <div class="inline-comments">
517 %for comment in comments:
517 %for comment in comments:
518 ${commentblock.comment_block(comment, inline=True)}
518 ${commentblock.comment_block(comment, inline=True)}
519 %endfor
519 %endfor
520 % if comments and comments[-1].outdated:
520 % if comments and comments[-1].outdated:
521 <span class="btn btn-secondary cb-comment-add-button comment-outdated}"
521 <span class="btn btn-secondary cb-comment-add-button comment-outdated}"
522 style="display: none;}">
522 style="display: none;}">
523 ${_('Add another comment')}
523 ${_('Add another comment')}
524 </span>
524 </span>
525 % else:
525 % else:
526 <span onclick="return Rhodecode.comments.createComment(this)"
526 <span onclick="return Rhodecode.comments.createComment(this)"
527 class="btn btn-secondary cb-comment-add-button">
527 class="btn btn-secondary cb-comment-add-button">
528 ${_('Add another comment')}
528 ${_('Add another comment')}
529 </span>
529 </span>
530 % endif
530 % endif
531
531
532 </div>
532 </div>
533 </%def>
533 </%def>
534
534
535 <%!
535 <%!
536 def get_comments_for(diff_type, comments, filename, line_version, line_number):
536 def get_comments_for(diff_type, comments, filename, line_version, line_number):
537 if hasattr(filename, 'unicode_path'):
537 if hasattr(filename, 'unicode_path'):
538 filename = filename.unicode_path
538 filename = filename.unicode_path
539
539
540 if not isinstance(filename, basestring):
540 if not isinstance(filename, basestring):
541 return None
541 return None
542
542
543 line_key = '{}{}'.format(line_version, line_number) ## e.g o37, n12
543 line_key = '{}{}'.format(line_version, line_number) ## e.g o37, n12
544
544
545 if comments and filename in comments:
545 if comments and filename in comments:
546 file_comments = comments[filename]
546 file_comments = comments[filename]
547 if line_key in file_comments:
547 if line_key in file_comments:
548 data = file_comments.pop(line_key)
548 data = file_comments.pop(line_key)
549 return data
549 return data
550 %>
550 %>
551
551
552 <%def name="render_hunk_lines_sideside(hunk, use_comments=False, inline_comments=None)">
552 <%def name="render_hunk_lines_sideside(hunk, use_comments=False, inline_comments=None)">
553
553
554 %for i, line in enumerate(hunk.sideside):
554 %for i, line in enumerate(hunk.sideside):
555 <%
555 <%
556 old_line_anchor, new_line_anchor = None, None
556 old_line_anchor, new_line_anchor = None, None
557 if line.original.lineno:
557 if line.original.lineno:
558 old_line_anchor = diff_line_anchor(hunk.source_file_path, line.original.lineno, 'o')
558 old_line_anchor = diff_line_anchor(hunk.source_file_path, line.original.lineno, 'o')
559 if line.modified.lineno:
559 if line.modified.lineno:
560 new_line_anchor = diff_line_anchor(hunk.target_file_path, line.modified.lineno, 'n')
560 new_line_anchor = diff_line_anchor(hunk.target_file_path, line.modified.lineno, 'n')
561 %>
561 %>
562
562
563 <tr class="cb-line">
563 <tr class="cb-line">
564 <td class="cb-data ${action_class(line.original.action)}"
564 <td class="cb-data ${action_class(line.original.action)}"
565 data-line-no="${line.original.lineno}"
565 data-line-no="${line.original.lineno}"
566 >
566 >
567 <div>
567 <div>
568
568
569 <% line_old_comments = None %>
569 <% line_old_comments = None %>
570 %if line.original.get_comment_args:
570 %if line.original.get_comment_args:
571 <% line_old_comments = get_comments_for('side-by-side', inline_comments, *line.original.get_comment_args) %>
571 <% line_old_comments = get_comments_for('side-by-side', inline_comments, *line.original.get_comment_args) %>
572 %endif
572 %endif
573 %if line_old_comments:
573 %if line_old_comments:
574 <% has_outdated = any([x.outdated for x in line_old_comments]) %>
574 <% has_outdated = any([x.outdated for x in line_old_comments]) %>
575 % if has_outdated:
575 % if has_outdated:
576 <i title="${_('comments including outdated')}:${len(line_old_comments)}" class="icon-comment_toggle" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
576 <i title="${_('comments including outdated')}:${len(line_old_comments)}" class="icon-comment_toggle" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
577 % else:
577 % else:
578 <i title="${_('comments')}: ${len(line_old_comments)}" class="icon-comment" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
578 <i title="${_('comments')}: ${len(line_old_comments)}" class="icon-comment" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
579 % endif
579 % endif
580 %endif
580 %endif
581 </div>
581 </div>
582 </td>
582 </td>
583 <td class="cb-lineno ${action_class(line.original.action)}"
583 <td class="cb-lineno ${action_class(line.original.action)}"
584 data-line-no="${line.original.lineno}"
584 data-line-no="${line.original.lineno}"
585 %if old_line_anchor:
585 %if old_line_anchor:
586 id="${old_line_anchor}"
586 id="${old_line_anchor}"
587 %endif
587 %endif
588 >
588 >
589 %if line.original.lineno:
589 %if line.original.lineno:
590 <a name="${old_line_anchor}" href="#${old_line_anchor}">${line.original.lineno}</a>
590 <a name="${old_line_anchor}" href="#${old_line_anchor}">${line.original.lineno}</a>
591 %endif
591 %endif
592 </td>
592 </td>
593 <td class="cb-content ${action_class(line.original.action)}"
593 <td class="cb-content ${action_class(line.original.action)}"
594 data-line-no="o${line.original.lineno}"
594 data-line-no="o${line.original.lineno}"
595 >
595 >
596 %if use_comments and line.original.lineno:
596 %if use_comments and line.original.lineno:
597 ${render_add_comment_button()}
597 ${render_add_comment_button()}
598 %endif
598 %endif
599 <span class="cb-code">${line.original.action} ${line.original.content or '' | n}</span>
599 <span class="cb-code">${line.original.action} ${line.original.content or '' | n}</span>
600
600
601 %if use_comments and line.original.lineno and line_old_comments:
601 %if use_comments and line.original.lineno and line_old_comments:
602 ${inline_comments_container(line_old_comments, inline_comments)}
602 ${inline_comments_container(line_old_comments, inline_comments)}
603 %endif
603 %endif
604
604
605 </td>
605 </td>
606 <td class="cb-data ${action_class(line.modified.action)}"
606 <td class="cb-data ${action_class(line.modified.action)}"
607 data-line-no="${line.modified.lineno}"
607 data-line-no="${line.modified.lineno}"
608 >
608 >
609 <div>
609 <div>
610
610
611 %if line.modified.get_comment_args:
611 %if line.modified.get_comment_args:
612 <% line_new_comments = get_comments_for('side-by-side', inline_comments, *line.modified.get_comment_args) %>
612 <% line_new_comments = get_comments_for('side-by-side', inline_comments, *line.modified.get_comment_args) %>
613 %else:
613 %else:
614 <% line_new_comments = None%>
614 <% line_new_comments = None%>
615 %endif
615 %endif
616 %if line_new_comments:
616 %if line_new_comments:
617 <% has_outdated = any([x.outdated for x in line_new_comments]) %>
617 <% has_outdated = any([x.outdated for x in line_new_comments]) %>
618 % if has_outdated:
618 % if has_outdated:
619 <i title="${_('comments including outdated')}:${len(line_new_comments)}" class="icon-comment_toggle" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
619 <i title="${_('comments including outdated')}:${len(line_new_comments)}" class="icon-comment_toggle" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
620 % else:
620 % else:
621 <i title="${_('comments')}: ${len(line_new_comments)}" class="icon-comment" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
621 <i title="${_('comments')}: ${len(line_new_comments)}" class="icon-comment" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
622 % endif
622 % endif
623 %endif
623 %endif
624 </div>
624 </div>
625 </td>
625 </td>
626 <td class="cb-lineno ${action_class(line.modified.action)}"
626 <td class="cb-lineno ${action_class(line.modified.action)}"
627 data-line-no="${line.modified.lineno}"
627 data-line-no="${line.modified.lineno}"
628 %if new_line_anchor:
628 %if new_line_anchor:
629 id="${new_line_anchor}"
629 id="${new_line_anchor}"
630 %endif
630 %endif
631 >
631 >
632 %if line.modified.lineno:
632 %if line.modified.lineno:
633 <a name="${new_line_anchor}" href="#${new_line_anchor}">${line.modified.lineno}</a>
633 <a name="${new_line_anchor}" href="#${new_line_anchor}">${line.modified.lineno}</a>
634 %endif
634 %endif
635 </td>
635 </td>
636 <td class="cb-content ${action_class(line.modified.action)}"
636 <td class="cb-content ${action_class(line.modified.action)}"
637 data-line-no="n${line.modified.lineno}"
637 data-line-no="n${line.modified.lineno}"
638 >
638 >
639 %if use_comments and line.modified.lineno:
639 %if use_comments and line.modified.lineno:
640 ${render_add_comment_button()}
640 ${render_add_comment_button()}
641 %endif
641 %endif
642 <span class="cb-code">${line.modified.action} ${line.modified.content or '' | n}</span>
642 <span class="cb-code">${line.modified.action} ${line.modified.content or '' | n}</span>
643 %if use_comments and line.modified.lineno and line_new_comments:
643 %if use_comments and line.modified.lineno and line_new_comments:
644 ${inline_comments_container(line_new_comments, inline_comments)}
644 ${inline_comments_container(line_new_comments, inline_comments)}
645 %endif
645 %endif
646 </td>
646 </td>
647 </tr>
647 </tr>
648 %endfor
648 %endfor
649 </%def>
649 </%def>
650
650
651
651
652 <%def name="render_hunk_lines_unified(hunk, use_comments=False, inline_comments=None)">
652 <%def name="render_hunk_lines_unified(hunk, use_comments=False, inline_comments=None)">
653 %for old_line_no, new_line_no, action, content, comments_args in hunk.unified:
653 %for old_line_no, new_line_no, action, content, comments_args in hunk.unified:
654 <%
654 <%
655 old_line_anchor, new_line_anchor = None, None
655 old_line_anchor, new_line_anchor = None, None
656 if old_line_no:
656 if old_line_no:
657 old_line_anchor = diff_line_anchor(hunk.source_file_path, old_line_no, 'o')
657 old_line_anchor = diff_line_anchor(hunk.source_file_path, old_line_no, 'o')
658 if new_line_no:
658 if new_line_no:
659 new_line_anchor = diff_line_anchor(hunk.target_file_path, new_line_no, 'n')
659 new_line_anchor = diff_line_anchor(hunk.target_file_path, new_line_no, 'n')
660 %>
660 %>
661 <tr class="cb-line">
661 <tr class="cb-line">
662 <td class="cb-data ${action_class(action)}">
662 <td class="cb-data ${action_class(action)}">
663 <div>
663 <div>
664
664
665 %if comments_args:
665 %if comments_args:
666 <% comments = get_comments_for('unified', inline_comments, *comments_args) %>
666 <% comments = get_comments_for('unified', inline_comments, *comments_args) %>
667 %else:
667 %else:
668 <% comments = None %>
668 <% comments = None %>
669 %endif
669 %endif
670
670
671 % if comments:
671 % if comments:
672 <% has_outdated = any([x.outdated for x in comments]) %>
672 <% has_outdated = any([x.outdated for x in comments]) %>
673 % if has_outdated:
673 % if has_outdated:
674 <i title="${_('comments including outdated')}:${len(comments)}" class="icon-comment_toggle" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
674 <i title="${_('comments including outdated')}:${len(comments)}" class="icon-comment_toggle" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
675 % else:
675 % else:
676 <i title="${_('comments')}: ${len(comments)}" class="icon-comment" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
676 <i title="${_('comments')}: ${len(comments)}" class="icon-comment" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
677 % endif
677 % endif
678 % endif
678 % endif
679 </div>
679 </div>
680 </td>
680 </td>
681 <td class="cb-lineno ${action_class(action)}"
681 <td class="cb-lineno ${action_class(action)}"
682 data-line-no="${old_line_no}"
682 data-line-no="${old_line_no}"
683 %if old_line_anchor:
683 %if old_line_anchor:
684 id="${old_line_anchor}"
684 id="${old_line_anchor}"
685 %endif
685 %endif
686 >
686 >
687 %if old_line_anchor:
687 %if old_line_anchor:
688 <a name="${old_line_anchor}" href="#${old_line_anchor}">${old_line_no}</a>
688 <a name="${old_line_anchor}" href="#${old_line_anchor}">${old_line_no}</a>
689 %endif
689 %endif
690 </td>
690 </td>
691 <td class="cb-lineno ${action_class(action)}"
691 <td class="cb-lineno ${action_class(action)}"
692 data-line-no="${new_line_no}"
692 data-line-no="${new_line_no}"
693 %if new_line_anchor:
693 %if new_line_anchor:
694 id="${new_line_anchor}"
694 id="${new_line_anchor}"
695 %endif
695 %endif
696 >
696 >
697 %if new_line_anchor:
697 %if new_line_anchor:
698 <a name="${new_line_anchor}" href="#${new_line_anchor}">${new_line_no}</a>
698 <a name="${new_line_anchor}" href="#${new_line_anchor}">${new_line_no}</a>
699 %endif
699 %endif
700 </td>
700 </td>
701 <td class="cb-content ${action_class(action)}"
701 <td class="cb-content ${action_class(action)}"
702 data-line-no="${(new_line_no and 'n' or 'o')}${(new_line_no or old_line_no)}"
702 data-line-no="${(new_line_no and 'n' or 'o')}${(new_line_no or old_line_no)}"
703 >
703 >
704 %if use_comments:
704 %if use_comments:
705 ${render_add_comment_button()}
705 ${render_add_comment_button()}
706 %endif
706 %endif
707 <span class="cb-code">${action} ${content or '' | n}</span>
707 <span class="cb-code">${action} ${content or '' | n}</span>
708 %if use_comments and comments:
708 %if use_comments and comments:
709 ${inline_comments_container(comments, inline_comments)}
709 ${inline_comments_container(comments, inline_comments)}
710 %endif
710 %endif
711 </td>
711 </td>
712 </tr>
712 </tr>
713 %endfor
713 %endfor
714 </%def>
714 </%def>
715
715
716
716
717 <%def name="render_hunk_lines(diff_mode, hunk, use_comments, inline_comments)">
717 <%def name="render_hunk_lines(diff_mode, hunk, use_comments, inline_comments)">
718 % if diff_mode == 'unified':
718 % if diff_mode == 'unified':
719 ${render_hunk_lines_unified(hunk, use_comments=use_comments, inline_comments=inline_comments)}
719 ${render_hunk_lines_unified(hunk, use_comments=use_comments, inline_comments=inline_comments)}
720 % elif diff_mode == 'sideside':
720 % elif diff_mode == 'sideside':
721 ${render_hunk_lines_sideside(hunk, use_comments=use_comments, inline_comments=inline_comments)}
721 ${render_hunk_lines_sideside(hunk, use_comments=use_comments, inline_comments=inline_comments)}
722 % else:
722 % else:
723 <tr class="cb-line">
723 <tr class="cb-line">
724 <td>unknown diff mode</td>
724 <td>unknown diff mode</td>
725 </tr>
725 </tr>
726 % endif
726 % endif
727 </%def>file changes
727 </%def>file changes
728
728
729
729
730 <%def name="render_add_comment_button()">
730 <%def name="render_add_comment_button()">
731 <button class="btn btn-small btn-primary cb-comment-box-opener" onclick="return Rhodecode.comments.createComment(this)">
731 <button class="btn btn-small btn-primary cb-comment-box-opener" onclick="return Rhodecode.comments.createComment(this)">
732 <span><i class="icon-comment"></i></span>
732 <span><i class="icon-comment"></i></span>
733 </button>
733 </button>
734 </%def>
734 </%def>
735
735
736 <%def name="render_diffset_menu(diffset=None, range_diff_on=None)">
736 <%def name="render_diffset_menu(diffset=None, range_diff_on=None)">
737
737
738 <div id="diff-file-sticky" class="diffset-menu clearinner">
738 <div id="diff-file-sticky" class="diffset-menu clearinner">
739 ## auto adjustable
739 ## auto adjustable
740 <div class="sidebar__inner">
740 <div class="sidebar__inner">
741 <div class="sidebar__bar">
741 <div class="sidebar__bar">
742 <div class="pull-right">
742 <div class="pull-right">
743 <div class="btn-group">
743 <div class="btn-group">
744
744
745 <a
745 <a
746 class="btn ${(c.user_session_attrs["diffmode"] == 'sideside' and 'btn-primary')} tooltip"
746 class="btn ${(c.user_session_attrs["diffmode"] == 'sideside' and 'btn-primary')} tooltip"
747 title="${h.tooltip(_('View side by side'))}"
747 title="${h.tooltip(_('View side by side'))}"
748 href="${h.current_route_path(request, diffmode='sideside')}">
748 href="${h.current_route_path(request, diffmode='sideside')}">
749 <span>${_('Side by Side')}</span>
749 <span>${_('Side by Side')}</span>
750 </a>
750 </a>
751 <a
751 <a
752 class="btn ${(c.user_session_attrs["diffmode"] == 'unified' and 'btn-primary')} tooltip"
752 class="btn ${(c.user_session_attrs["diffmode"] == 'unified' and 'btn-primary')} tooltip"
753 title="${h.tooltip(_('View unified'))}" href="${h.current_route_path(request, diffmode='unified')}">
753 title="${h.tooltip(_('View unified'))}" href="${h.current_route_path(request, diffmode='unified')}">
754 <span>${_('Unified')}</span>
754 <span>${_('Unified')}</span>
755 </a>
755 </a>
756 % if range_diff_on is True:
756 % if range_diff_on is True:
757 <a
757 <a
758 title="${_('Turn off: Show the diff as commit range')}"
758 title="${_('Turn off: Show the diff as commit range')}"
759 class="btn btn-primary"
759 class="btn btn-primary"
760 href="${h.current_route_path(request, **{"range-diff":"0"})}">
760 href="${h.current_route_path(request, **{"range-diff":"0"})}">
761 <span>${_('Range Diff')}</span>
761 <span>${_('Range Diff')}</span>
762 </a>
762 </a>
763 % elif range_diff_on is False:
763 % elif range_diff_on is False:
764 <a
764 <a
765 title="${_('Show the diff as commit range')}"
765 title="${_('Show the diff as commit range')}"
766 class="btn"
766 class="btn"
767 href="${h.current_route_path(request, **{"range-diff":"1"})}">
767 href="${h.current_route_path(request, **{"range-diff":"1"})}">
768 <span>${_('Range Diff')}</span>
768 <span>${_('Range Diff')}</span>
769 </a>
769 </a>
770 % endif
770 % endif
771 </div>
771 </div>
772 </div>
772 </div>
773 <div class="pull-left">
773 <div class="pull-left">
774 <div class="btn-group">
774 <div class="btn-group">
775 <div class="pull-left">
775 <div class="pull-left">
776 ${h.hidden('file_filter')}
776 ${h.hidden('file_filter')}
777 </div>
777 </div>
778 <a
778 <a
779 class="btn"
779 class="btn"
780 href="#"
780 href="#"
781 onclick="$('input[class=filediff-collapse-state]').prop('checked', false); Waypoint.refreshAll(); return false">${_('Expand All Files')}</a>
781 onclick="$('input[class=filediff-collapse-state]').prop('checked', false); updateSticky(); return false">${_('Expand All Files')}</a>
782 <a
782 <a
783 class="btn"
783 class="btn"
784 href="#"
784 href="#"
785 onclick="$('input[class=filediff-collapse-state]').prop('checked', true); Waypoint.refreshAll(); return false">${_('Collapse All Files')}</a>
785 onclick="$('input[class=filediff-collapse-state]').prop('checked', true); updateSticky(); return false">${_('Collapse All Files')}</a>
786 <a
786 <a
787 class="btn"
787 class="btn"
788 href="#"
788 href="#"
789 onclick="updateSticky();return Rhodecode.comments.toggleWideMode(this)">${_('Wide Mode Diff')}</a>
789 onclick="updateSticky();return Rhodecode.comments.toggleWideMode(this)">${_('Wide Mode Diff')}</a>
790
790
791 </div>
791 </div>
792 </div>
792 </div>
793 </div>
793 </div>
794 <div class="fpath-placeholder">
794 <div class="fpath-placeholder">
795 <i class="icon-file-text"></i>
795 <i class="icon-file-text"></i>
796 <strong class="fpath-placeholder-text">
796 <strong class="fpath-placeholder-text">
797 Context file:
797 Context file:
798 </strong>
798 </strong>
799 </div>
799 </div>
800 <div class="sidebar_inner_shadow"></div>
800 <div class="sidebar_inner_shadow"></div>
801 </div>
801 </div>
802 </div>
802 </div>
803
803
804 % if diffset:
804 % if diffset:
805
805
806 %if diffset.limited_diff:
806 %if diffset.limited_diff:
807 <% file_placeholder = _ungettext('%(num)s file changed', '%(num)s files changed', diffset.changed_files) % {'num': diffset.changed_files} %>
807 <% file_placeholder = _ungettext('%(num)s file changed', '%(num)s files changed', diffset.changed_files) % {'num': diffset.changed_files} %>
808 %else:
808 %else:
809 <% file_placeholder = _ungettext('%(num)s file changed: %(linesadd)s inserted, ''%(linesdel)s deleted', '%(num)s files changed: %(linesadd)s inserted, %(linesdel)s deleted', diffset.changed_files) % {'num': diffset.changed_files, 'linesadd': diffset.lines_added, 'linesdel': diffset.lines_deleted}%>
809 <% file_placeholder = _ungettext('%(num)s file changed: %(linesadd)s inserted, ''%(linesdel)s deleted', '%(num)s files changed: %(linesadd)s inserted, %(linesdel)s deleted', diffset.changed_files) % {'num': diffset.changed_files, 'linesadd': diffset.lines_added, 'linesdel': diffset.lines_deleted}%>
810 %endif
810 %endif
811 ## case on range-diff placeholder needs to be updated
811 ## case on range-diff placeholder needs to be updated
812 % if range_diff_on is True:
812 % if range_diff_on is True:
813 <% file_placeholder = _('Disabled on range diff') %>
813 <% file_placeholder = _('Disabled on range diff') %>
814 % endif
814 % endif
815
815
816 <script>
816 <script>
817
817
818 var feedFilesOptions = function (query, initialData) {
818 var feedFilesOptions = function (query, initialData) {
819 var data = {results: []};
819 var data = {results: []};
820 var isQuery = typeof query.term !== 'undefined';
820 var isQuery = typeof query.term !== 'undefined';
821
821
822 var section = _gettext('Changed files');
822 var section = _gettext('Changed files');
823 var filteredData = [];
823 var filteredData = [];
824
824
825 //filter results
825 //filter results
826 $.each(initialData.results, function (idx, value) {
826 $.each(initialData.results, function (idx, value) {
827
827
828 if (!isQuery || query.term.length === 0 || value.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0) {
828 if (!isQuery || query.term.length === 0 || value.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0) {
829 filteredData.push({
829 filteredData.push({
830 'id': this.id,
830 'id': this.id,
831 'text': this.text,
831 'text': this.text,
832 "ops": this.ops,
832 "ops": this.ops,
833 })
833 })
834 }
834 }
835
835
836 });
836 });
837
837
838 data.results = filteredData;
838 data.results = filteredData;
839
839
840 query.callback(data);
840 query.callback(data);
841 };
841 };
842
842
843 var formatFileResult = function(result, container, query, escapeMarkup) {
843 var formatFileResult = function(result, container, query, escapeMarkup) {
844 return function(data, escapeMarkup) {
844 return function(data, escapeMarkup) {
845 var container = '<div class="filelist" style="padding-right:100px">{0}</div>';
845 var container = '<div class="filelist" style="padding-right:100px">{0}</div>';
846 var tmpl = '<span style="margin-right:-50px"><strong>{0}</strong></span>'.format(escapeMarkup(data['text']));
846 var tmpl = '<span style="margin-right:-50px"><strong>{0}</strong></span>'.format(escapeMarkup(data['text']));
847 var pill = '<span class="pill-group" style="float: right;margin-right: -100px">' +
847 var pill = '<span class="pill-group" style="float: right;margin-right: -100px">' +
848 '<span class="pill" op="added">{0}</span>' +
848 '<span class="pill" op="added">{0}</span>' +
849 '<span class="pill" op="deleted">{1}</span>' +
849 '<span class="pill" op="deleted">{1}</span>' +
850 '</span>'
850 '</span>'
851 ;
851 ;
852 var added = data['ops']['added'];
852 var added = data['ops']['added'];
853 if (added === 0) {
853 if (added === 0) {
854 // don't show +0
854 // don't show +0
855 added = 0;
855 added = 0;
856 } else {
856 } else {
857 added = '+' + added;
857 added = '+' + added;
858 }
858 }
859
859
860 var deleted = -1*data['ops']['deleted'];
860 var deleted = -1*data['ops']['deleted'];
861
861
862 tmpl += pill.format(added, deleted);
862 tmpl += pill.format(added, deleted);
863 return container.format(tmpl);
863 return container.format(tmpl);
864
864
865 }(result, escapeMarkup);
865 }(result, escapeMarkup);
866 };
866 };
867 var preloadData = {
867 var preloadData = {
868 results: [
868 results: [
869 % for filediff in diffset.files:
869 % for filediff in diffset.files:
870 {id:"a_${h.FID(filediff.raw_id, filediff.patch['filename'])}",
870 {id:"a_${h.FID(filediff.raw_id, filediff.patch['filename'])}",
871 text:"${filediff.patch['filename']}",
871 text:"${filediff.patch['filename']}",
872 ops:${h.json.dumps(filediff.patch['stats'])|n}}${('' if loop.last else ',')}
872 ops:${h.json.dumps(filediff.patch['stats'])|n}}${('' if loop.last else ',')}
873 % endfor
873 % endfor
874 ]
874 ]
875 };
875 };
876
876
877 $(document).ready(function () {
877 $(document).ready(function () {
878
878
879 var fileFilter = $("#file_filter").select2({
879 var fileFilter = $("#file_filter").select2({
880 'dropdownAutoWidth': true,
880 'dropdownAutoWidth': true,
881 'width': 'auto',
881 'width': 'auto',
882 'placeholder': "${file_placeholder}",
882 'placeholder': "${file_placeholder}",
883 containerCssClass: "drop-menu",
883 containerCssClass: "drop-menu",
884 dropdownCssClass: "drop-menu-dropdown",
884 dropdownCssClass: "drop-menu-dropdown",
885 data: preloadData,
885 data: preloadData,
886 query: function(query) {
886 query: function(query) {
887 feedFilesOptions(query, preloadData);
887 feedFilesOptions(query, preloadData);
888 },
888 },
889 formatResult: formatFileResult
889 formatResult: formatFileResult
890 });
890 });
891 % if range_diff_on is True:
891 % if range_diff_on is True:
892 fileFilter.select2("enable", false);
892 fileFilter.select2("enable", false);
893
893
894 % endif
894 % endif
895
895
896 $("#file_filter").on('click', function (e) {
896 $("#file_filter").on('click', function (e) {
897 e.preventDefault();
897 e.preventDefault();
898 var selected = $('#file_filter').select2('data');
898 var selected = $('#file_filter').select2('data');
899 var idSelector = "#"+selected.id;
899 var idSelector = "#"+selected.id;
900 window.location.hash = idSelector;
900 window.location.hash = idSelector;
901 // expand the container if we quick-select the field
901 // expand the container if we quick-select the field
902 $(idSelector).next().prop('checked', false);
902 $(idSelector).next().prop('checked', false);
903 Waypoint.refreshAll()
903 updateSticky()
904 });
904 });
905
905
906 var contextPrefix = _gettext('Context file: ');
906 var contextPrefix = _gettext('Context file: ');
907 ## sticky sidebar
907 ## sticky sidebar
908 var sidebarElement = document.getElementById('diff-file-sticky');
908 var sidebarElement = document.getElementById('diff-file-sticky');
909 sidebar = new StickySidebar(sidebarElement, {
909 sidebar = new StickySidebar(sidebarElement, {
910 topSpacing: 0,
910 topSpacing: 0,
911 bottomSpacing: 0,
911 bottomSpacing: 0,
912 innerWrapperSelector: '.sidebar__inner'
912 innerWrapperSelector: '.sidebar__inner'
913 });
913 });
914 sidebarElement.addEventListener('affixed.static.stickySidebar', function () {
914 sidebarElement.addEventListener('affixed.static.stickySidebar', function () {
915 // reset our file so it's not holding new value
915 // reset our file so it's not holding new value
916 $('.fpath-placeholder-text').html(contextPrefix)
916 $('.fpath-placeholder-text').html(contextPrefix)
917 });
917 });
918
918
919 updateSticky = function () {
919 updateSticky = function () {
920 sidebar.updateSticky()
920 sidebar.updateSticky();
921 Waypoint.refreshAll();
921 };
922 };
922
923
923 var animateText = $.debounce(100, function(fPath, anchorId) {
924 var animateText = $.debounce(100, function(fPath, anchorId) {
924 // animate setting the text
925 // animate setting the text
925 var callback = function () {
926 var callback = function () {
926 $('.fpath-placeholder-text').animate({'opacity': 1.00}, 200)
927 $('.fpath-placeholder-text').animate({'opacity': 1.00}, 200)
927 $('.fpath-placeholder-text').html(contextPrefix + '<a href="#a_' + anchorId + '">' + fPath + '</a>')
928 $('.fpath-placeholder-text').html(contextPrefix + '<a href="#a_' + anchorId + '">' + fPath + '</a>')
928 };
929 };
929 $('.fpath-placeholder-text').animate({'opacity': 0.15}, 200, callback);
930 $('.fpath-placeholder-text').animate({'opacity': 0.15}, 200, callback);
930 });
931 });
931
932
932 ## dynamic file waypoints
933 ## dynamic file waypoints
933 var setFPathInfo = function(fPath, anchorId){
934 var setFPathInfo = function(fPath, anchorId){
934 animateText(fPath, anchorId)
935 animateText(fPath, anchorId)
935 };
936 };
936
937
937 var codeBlock = $('.filediff');
938 var codeBlock = $('.filediff');
938 // forward waypoint
939 // forward waypoint
939 codeBlock.waypoint(
940 codeBlock.waypoint(
940 function(direction) {
941 function(direction) {
941 if (direction === "down"){
942 if (direction === "down"){
942 setFPathInfo($(this.element).data('fPath'), $(this.element).data('anchorId'))
943 setFPathInfo($(this.element).data('fPath'), $(this.element).data('anchorId'))
943 }
944 }
944 }, {
945 }, {
945 offset: 70,
946 offset: 70,
946 context: '.fpath-placeholder'
947 context: '.fpath-placeholder'
947 }
948 }
948 );
949 );
949
950
950 // backward waypoint
951 // backward waypoint
951 codeBlock.waypoint(
952 codeBlock.waypoint(
952 function(direction) {
953 function(direction) {
953 if (direction === "up"){
954 if (direction === "up"){
954 setFPathInfo($(this.element).data('fPath'), $(this.element).data('anchorId'))
955 setFPathInfo($(this.element).data('fPath'), $(this.element).data('anchorId'))
955 }
956 }
956 }, {
957 }, {
957 offset: function () {
958 offset: function () {
958 return -this.element.clientHeight + 90
959 return -this.element.clientHeight + 90
959 },
960 },
960 context: '.fpath-placeholder'
961 context: '.fpath-placeholder'
961 }
962 }
962 );
963 );
963
964
964 });
965 });
965
966
966 </script>
967 </script>
967 % endif
968 % endif
968
969
969 </%def> No newline at end of file
970 </%def>
General Comments 0
You need to be logged in to leave comments. Login now