##// END OF EJS Templates
comments: unlock submit if we use slash commands to set status.
marcink -
r1444:81cfc013 default
parent child Browse files
Show More
@@ -1,813 +1,813 b''
1 // # Copyright (C) 2010-2017 RhodeCode GmbH
1 // # Copyright (C) 2010-2017 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 /* Comment form for main and inline comments */
45 /* Comment form for main and inline comments */
46 (function(mod) {
46 (function(mod) {
47
47
48 if (typeof exports == "object" && typeof module == "object") {
48 if (typeof exports == "object" && typeof module == "object") {
49 // CommonJS
49 // CommonJS
50 module.exports = mod();
50 module.exports = mod();
51 }
51 }
52 else {
52 else {
53 // Plain browser env
53 // Plain browser env
54 (this || window).CommentForm = mod();
54 (this || window).CommentForm = mod();
55 }
55 }
56
56
57 })(function() {
57 })(function() {
58 "use strict";
58 "use strict";
59
59
60 function CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId) {
60 function CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId) {
61 if (!(this instanceof CommentForm)) {
61 if (!(this instanceof CommentForm)) {
62 return new CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId);
62 return new CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId);
63 }
63 }
64
64
65 // bind the element instance to our Form
65 // bind the element instance to our Form
66 $(formElement).get(0).CommentForm = this;
66 $(formElement).get(0).CommentForm = this;
67
67
68 this.withLineNo = function(selector) {
68 this.withLineNo = function(selector) {
69 var lineNo = this.lineNo;
69 var lineNo = this.lineNo;
70 if (lineNo === undefined) {
70 if (lineNo === undefined) {
71 return selector
71 return selector
72 } else {
72 } else {
73 return selector + '_' + lineNo;
73 return selector + '_' + lineNo;
74 }
74 }
75 };
75 };
76
76
77 this.commitId = commitId;
77 this.commitId = commitId;
78 this.pullRequestId = pullRequestId;
78 this.pullRequestId = pullRequestId;
79 this.lineNo = lineNo;
79 this.lineNo = lineNo;
80 this.initAutocompleteActions = initAutocompleteActions;
80 this.initAutocompleteActions = initAutocompleteActions;
81
81
82 this.previewButton = this.withLineNo('#preview-btn');
82 this.previewButton = this.withLineNo('#preview-btn');
83 this.previewContainer = this.withLineNo('#preview-container');
83 this.previewContainer = this.withLineNo('#preview-container');
84
84
85 this.previewBoxSelector = this.withLineNo('#preview-box');
85 this.previewBoxSelector = this.withLineNo('#preview-box');
86
86
87 this.editButton = this.withLineNo('#edit-btn');
87 this.editButton = this.withLineNo('#edit-btn');
88 this.editContainer = this.withLineNo('#edit-container');
88 this.editContainer = this.withLineNo('#edit-container');
89 this.cancelButton = this.withLineNo('#cancel-btn');
89 this.cancelButton = this.withLineNo('#cancel-btn');
90 this.commentType = this.withLineNo('#comment_type');
90 this.commentType = this.withLineNo('#comment_type');
91
91
92 this.resolvesId = null;
92 this.resolvesId = null;
93 this.resolvesActionId = null;
93 this.resolvesActionId = null;
94
94
95 this.cmBox = this.withLineNo('#text');
95 this.cmBox = this.withLineNo('#text');
96 this.cm = initCommentBoxCodeMirror(this, this.cmBox, this.initAutocompleteActions);
96 this.cm = initCommentBoxCodeMirror(this, this.cmBox, this.initAutocompleteActions);
97
97
98 this.statusChange = this.withLineNo('#change_status');
98 this.statusChange = this.withLineNo('#change_status');
99
99
100 this.submitForm = formElement;
100 this.submitForm = formElement;
101 this.submitButton = $(this.submitForm).find('input[type="submit"]');
101 this.submitButton = $(this.submitForm).find('input[type="submit"]');
102 this.submitButtonText = this.submitButton.val();
102 this.submitButtonText = this.submitButton.val();
103
103
104 this.previewUrl = pyroutes.url('changeset_comment_preview',
104 this.previewUrl = pyroutes.url('changeset_comment_preview',
105 {'repo_name': templateContext.repo_name});
105 {'repo_name': templateContext.repo_name});
106
106
107 if (resolvesCommentId){
107 if (resolvesCommentId){
108 this.resolvesId = '#resolve_comment_{0}'.format(resolvesCommentId);
108 this.resolvesId = '#resolve_comment_{0}'.format(resolvesCommentId);
109 this.resolvesActionId = '#resolve_comment_action_{0}'.format(resolvesCommentId);
109 this.resolvesActionId = '#resolve_comment_action_{0}'.format(resolvesCommentId);
110 $(this.commentType).prop('disabled', true);
110 $(this.commentType).prop('disabled', true);
111 $(this.commentType).addClass('disabled');
111 $(this.commentType).addClass('disabled');
112
112
113 // disable select
113 // disable select
114 setTimeout(function() {
114 setTimeout(function() {
115 $(self.statusChange).select2('readonly', true);
115 $(self.statusChange).select2('readonly', true);
116 }, 10);
116 }, 10);
117
117
118 var resolvedInfo = (
118 var resolvedInfo = (
119 '<li class="resolve-action">' +
119 '<li class="resolve-action">' +
120 '<input type="hidden" id="resolve_comment_{0}" name="resolve_comment_{0}" value="{0}">' +
120 '<input type="hidden" id="resolve_comment_{0}" name="resolve_comment_{0}" value="{0}">' +
121 '<button id="resolve_comment_action_{0}" class="resolve-text btn btn-sm" onclick="return Rhodecode.comments.submitResolution({0})">{1} #{0}</button>' +
121 '<button id="resolve_comment_action_{0}" class="resolve-text btn btn-sm" onclick="return Rhodecode.comments.submitResolution({0})">{1} #{0}</button>' +
122 '</li>'
122 '</li>'
123 ).format(resolvesCommentId, _gettext('resolve comment'));
123 ).format(resolvesCommentId, _gettext('resolve comment'));
124 $(resolvedInfo).insertAfter($(this.commentType).parent());
124 $(resolvedInfo).insertAfter($(this.commentType).parent());
125 }
125 }
126
126
127 // based on commitId, or pullRequestId decide where do we submit
127 // based on commitId, or pullRequestId decide where do we submit
128 // out data
128 // out data
129 if (this.commitId){
129 if (this.commitId){
130 this.submitUrl = pyroutes.url('changeset_comment',
130 this.submitUrl = pyroutes.url('changeset_comment',
131 {'repo_name': templateContext.repo_name,
131 {'repo_name': templateContext.repo_name,
132 'revision': this.commitId});
132 'revision': this.commitId});
133 this.selfUrl = pyroutes.url('changeset_home',
133 this.selfUrl = pyroutes.url('changeset_home',
134 {'repo_name': templateContext.repo_name,
134 {'repo_name': templateContext.repo_name,
135 'revision': this.commitId});
135 'revision': this.commitId});
136
136
137 } else if (this.pullRequestId) {
137 } else if (this.pullRequestId) {
138 this.submitUrl = pyroutes.url('pullrequest_comment',
138 this.submitUrl = pyroutes.url('pullrequest_comment',
139 {'repo_name': templateContext.repo_name,
139 {'repo_name': templateContext.repo_name,
140 'pull_request_id': this.pullRequestId});
140 'pull_request_id': this.pullRequestId});
141 this.selfUrl = pyroutes.url('pullrequest_show',
141 this.selfUrl = pyroutes.url('pullrequest_show',
142 {'repo_name': templateContext.repo_name,
142 {'repo_name': templateContext.repo_name,
143 'pull_request_id': this.pullRequestId});
143 'pull_request_id': this.pullRequestId});
144
144
145 } else {
145 } else {
146 throw new Error(
146 throw new Error(
147 'CommentForm requires pullRequestId, or commitId to be specified.')
147 'CommentForm requires pullRequestId, or commitId to be specified.')
148 }
148 }
149
149
150 // FUNCTIONS and helpers
150 // FUNCTIONS and helpers
151 var self = this;
151 var self = this;
152
152
153 this.isInline = function(){
153 this.isInline = function(){
154 return this.lineNo && this.lineNo != 'general';
154 return this.lineNo && this.lineNo != 'general';
155 };
155 };
156
156
157 this.getCmInstance = function(){
157 this.getCmInstance = function(){
158 return this.cm
158 return this.cm
159 };
159 };
160
160
161 this.setPlaceholder = function(placeholder) {
161 this.setPlaceholder = function(placeholder) {
162 var cm = this.getCmInstance();
162 var cm = this.getCmInstance();
163 if (cm){
163 if (cm){
164 cm.setOption('placeholder', placeholder);
164 cm.setOption('placeholder', placeholder);
165 }
165 }
166 };
166 };
167
167
168 this.getCommentStatus = function() {
168 this.getCommentStatus = function() {
169 return $(this.submitForm).find(this.statusChange).val();
169 return $(this.submitForm).find(this.statusChange).val();
170 };
170 };
171 this.getCommentType = function() {
171 this.getCommentType = function() {
172 return $(this.submitForm).find(this.commentType).val();
172 return $(this.submitForm).find(this.commentType).val();
173 };
173 };
174
174
175 this.getResolvesId = function() {
175 this.getResolvesId = function() {
176 return $(this.submitForm).find(this.resolvesId).val() || null;
176 return $(this.submitForm).find(this.resolvesId).val() || null;
177 };
177 };
178 this.markCommentResolved = function(resolvedCommentId){
178 this.markCommentResolved = function(resolvedCommentId){
179 $('#comment-label-{0}'.format(resolvedCommentId)).find('.resolved').show();
179 $('#comment-label-{0}'.format(resolvedCommentId)).find('.resolved').show();
180 $('#comment-label-{0}'.format(resolvedCommentId)).find('.resolve').hide();
180 $('#comment-label-{0}'.format(resolvedCommentId)).find('.resolve').hide();
181 };
181 };
182
182
183 this.isAllowedToSubmit = function() {
183 this.isAllowedToSubmit = function() {
184 return !$(this.submitButton).prop('disabled');
184 return !$(this.submitButton).prop('disabled');
185 };
185 };
186
186
187 this.initStatusChangeSelector = function(){
187 this.initStatusChangeSelector = function(){
188 var formatChangeStatus = function(state, escapeMarkup) {
188 var formatChangeStatus = function(state, escapeMarkup) {
189 var originalOption = state.element;
189 var originalOption = state.element;
190 return '<div class="flag_status ' + $(originalOption).data('status') + ' pull-left"></div>' +
190 return '<div class="flag_status ' + $(originalOption).data('status') + ' pull-left"></div>' +
191 '<span>' + escapeMarkup(state.text) + '</span>';
191 '<span>' + escapeMarkup(state.text) + '</span>';
192 };
192 };
193 var formatResult = function(result, container, query, escapeMarkup) {
193 var formatResult = function(result, container, query, escapeMarkup) {
194 return formatChangeStatus(result, escapeMarkup);
194 return formatChangeStatus(result, escapeMarkup);
195 };
195 };
196
196
197 var formatSelection = function(data, container, escapeMarkup) {
197 var formatSelection = function(data, container, escapeMarkup) {
198 return formatChangeStatus(data, escapeMarkup);
198 return formatChangeStatus(data, escapeMarkup);
199 };
199 };
200
200
201 $(this.submitForm).find(this.statusChange).select2({
201 $(this.submitForm).find(this.statusChange).select2({
202 placeholder: _gettext('Status Review'),
202 placeholder: _gettext('Status Review'),
203 formatResult: formatResult,
203 formatResult: formatResult,
204 formatSelection: formatSelection,
204 formatSelection: formatSelection,
205 containerCssClass: "drop-menu status_box_menu",
205 containerCssClass: "drop-menu status_box_menu",
206 dropdownCssClass: "drop-menu-dropdown",
206 dropdownCssClass: "drop-menu-dropdown",
207 dropdownAutoWidth: true,
207 dropdownAutoWidth: true,
208 minimumResultsForSearch: -1
208 minimumResultsForSearch: -1
209 });
209 });
210 $(this.submitForm).find(this.statusChange).on('change', function() {
210 $(this.submitForm).find(this.statusChange).on('change', function() {
211 var status = self.getCommentStatus();
211 var status = self.getCommentStatus();
212 if (status && !self.isInline()) {
212 if (status && !self.isInline()) {
213 $(self.submitButton).prop('disabled', false);
213 $(self.submitButton).prop('disabled', false);
214 }
214 }
215
215
216 var placeholderText = _gettext('Comment text will be set automatically based on currently selected status ({0}) ...').format(status);
216 var placeholderText = _gettext('Comment text will be set automatically based on currently selected status ({0}) ...').format(status);
217 self.setPlaceholder(placeholderText)
217 self.setPlaceholder(placeholderText)
218 })
218 })
219 };
219 };
220
220
221 // reset the comment form into it's original state
221 // reset the comment form into it's original state
222 this.resetCommentFormState = function(content) {
222 this.resetCommentFormState = function(content) {
223 content = content || '';
223 content = content || '';
224
224
225 $(this.editContainer).show();
225 $(this.editContainer).show();
226 $(this.editButton).parent().addClass('active');
226 $(this.editButton).parent().addClass('active');
227
227
228 $(this.previewContainer).hide();
228 $(this.previewContainer).hide();
229 $(this.previewButton).parent().removeClass('active');
229 $(this.previewButton).parent().removeClass('active');
230
230
231 this.setActionButtonsDisabled(true);
231 this.setActionButtonsDisabled(true);
232 self.cm.setValue(content);
232 self.cm.setValue(content);
233 self.cm.setOption("readOnly", false);
233 self.cm.setOption("readOnly", false);
234
234
235 if (this.resolvesId) {
235 if (this.resolvesId) {
236 // destroy the resolve action
236 // destroy the resolve action
237 $(this.resolvesId).parent().remove();
237 $(this.resolvesId).parent().remove();
238 }
238 }
239
239
240 $(this.statusChange).select2('readonly', false);
240 $(this.statusChange).select2('readonly', false);
241 };
241 };
242
242
243 this.globalSubmitSuccessCallback = function(){
243 this.globalSubmitSuccessCallback = function(){
244 // default behaviour is to call GLOBAL hook, if it's registered.
244 // default behaviour is to call GLOBAL hook, if it's registered.
245 if (window.commentFormGlobalSubmitSuccessCallback !== undefined){
245 if (window.commentFormGlobalSubmitSuccessCallback !== undefined){
246 commentFormGlobalSubmitSuccessCallback()
246 commentFormGlobalSubmitSuccessCallback()
247 }
247 }
248 };
248 };
249
249
250 this.submitAjaxPOST = function(url, postData, successHandler, failHandler) {
250 this.submitAjaxPOST = function(url, postData, successHandler, failHandler) {
251 failHandler = failHandler || function() {};
251 failHandler = failHandler || function() {};
252 var postData = toQueryString(postData);
252 var postData = toQueryString(postData);
253 var request = $.ajax({
253 var request = $.ajax({
254 url: url,
254 url: url,
255 type: 'POST',
255 type: 'POST',
256 data: postData,
256 data: postData,
257 headers: {'X-PARTIAL-XHR': true}
257 headers: {'X-PARTIAL-XHR': true}
258 })
258 })
259 .done(function(data) {
259 .done(function(data) {
260 successHandler(data);
260 successHandler(data);
261 })
261 })
262 .fail(function(data, textStatus, errorThrown){
262 .fail(function(data, textStatus, errorThrown){
263 alert(
263 alert(
264 "Error while submitting comment.\n" +
264 "Error while submitting comment.\n" +
265 "Error code {0} ({1}).".format(data.status, data.statusText));
265 "Error code {0} ({1}).".format(data.status, data.statusText));
266 failHandler()
266 failHandler()
267 });
267 });
268 return request;
268 return request;
269 };
269 };
270
270
271 // overwrite a submitHandler, we need to do it for inline comments
271 // overwrite a submitHandler, we need to do it for inline comments
272 this.setHandleFormSubmit = function(callback) {
272 this.setHandleFormSubmit = function(callback) {
273 this.handleFormSubmit = callback;
273 this.handleFormSubmit = callback;
274 };
274 };
275
275
276 // overwrite a submitSuccessHandler
276 // overwrite a submitSuccessHandler
277 this.setGlobalSubmitSuccessCallback = function(callback) {
277 this.setGlobalSubmitSuccessCallback = function(callback) {
278 this.globalSubmitSuccessCallback = callback;
278 this.globalSubmitSuccessCallback = callback;
279 };
279 };
280
280
281 // default handler for for submit for main comments
281 // default handler for for submit for main comments
282 this.handleFormSubmit = function() {
282 this.handleFormSubmit = function() {
283 var text = self.cm.getValue();
283 var text = self.cm.getValue();
284 var status = self.getCommentStatus();
284 var status = self.getCommentStatus();
285 var commentType = self.getCommentType();
285 var commentType = self.getCommentType();
286 var resolvesCommentId = self.getResolvesId();
286 var resolvesCommentId = self.getResolvesId();
287
287
288 if (text === "" && !status) {
288 if (text === "" && !status) {
289 return;
289 return;
290 }
290 }
291
291
292 var excludeCancelBtn = false;
292 var excludeCancelBtn = false;
293 var submitEvent = true;
293 var submitEvent = true;
294 self.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
294 self.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
295 self.cm.setOption("readOnly", true);
295 self.cm.setOption("readOnly", true);
296
296
297 var postData = {
297 var postData = {
298 'text': text,
298 'text': text,
299 'changeset_status': status,
299 'changeset_status': status,
300 'comment_type': commentType,
300 'comment_type': commentType,
301 'csrf_token': CSRF_TOKEN
301 'csrf_token': CSRF_TOKEN
302 };
302 };
303 if (resolvesCommentId){
303 if (resolvesCommentId){
304 postData['resolves_comment_id'] = resolvesCommentId;
304 postData['resolves_comment_id'] = resolvesCommentId;
305 }
305 }
306
306
307 var submitSuccessCallback = function(o) {
307 var submitSuccessCallback = function(o) {
308 // reload page if we change status for single commit.
308 // reload page if we change status for single commit.
309 if (status && self.commitId) {
309 if (status && self.commitId) {
310 location.reload(true);
310 location.reload(true);
311 } else {
311 } else {
312 $('#injected_page_comments').append(o.rendered_text);
312 $('#injected_page_comments').append(o.rendered_text);
313 self.resetCommentFormState();
313 self.resetCommentFormState();
314 timeagoActivate();
314 timeagoActivate();
315
315
316 // mark visually which comment was resolved
316 // mark visually which comment was resolved
317 if (resolvesCommentId) {
317 if (resolvesCommentId) {
318 self.markCommentResolved(resolvesCommentId);
318 self.markCommentResolved(resolvesCommentId);
319 }
319 }
320 }
320 }
321
321
322 // run global callback on submit
322 // run global callback on submit
323 self.globalSubmitSuccessCallback();
323 self.globalSubmitSuccessCallback();
324
324
325 };
325 };
326 var submitFailCallback = function(){
326 var submitFailCallback = function(){
327 self.resetCommentFormState(text);
327 self.resetCommentFormState(text);
328 };
328 };
329 self.submitAjaxPOST(
329 self.submitAjaxPOST(
330 self.submitUrl, postData, submitSuccessCallback, submitFailCallback);
330 self.submitUrl, postData, submitSuccessCallback, submitFailCallback);
331 };
331 };
332
332
333 this.previewSuccessCallback = function(o) {
333 this.previewSuccessCallback = function(o) {
334 $(self.previewBoxSelector).html(o);
334 $(self.previewBoxSelector).html(o);
335 $(self.previewBoxSelector).removeClass('unloaded');
335 $(self.previewBoxSelector).removeClass('unloaded');
336
336
337 // swap buttons, making preview active
337 // swap buttons, making preview active
338 $(self.previewButton).parent().addClass('active');
338 $(self.previewButton).parent().addClass('active');
339 $(self.editButton).parent().removeClass('active');
339 $(self.editButton).parent().removeClass('active');
340
340
341 // unlock buttons
341 // unlock buttons
342 self.setActionButtonsDisabled(false);
342 self.setActionButtonsDisabled(false);
343 };
343 };
344
344
345 this.setActionButtonsDisabled = function(state, excludeCancelBtn, submitEvent) {
345 this.setActionButtonsDisabled = function(state, excludeCancelBtn, submitEvent) {
346 excludeCancelBtn = excludeCancelBtn || false;
346 excludeCancelBtn = excludeCancelBtn || false;
347 submitEvent = submitEvent || false;
347 submitEvent = submitEvent || false;
348
348
349 $(this.editButton).prop('disabled', state);
349 $(this.editButton).prop('disabled', state);
350 $(this.previewButton).prop('disabled', state);
350 $(this.previewButton).prop('disabled', state);
351
351
352 if (!excludeCancelBtn) {
352 if (!excludeCancelBtn) {
353 $(this.cancelButton).prop('disabled', state);
353 $(this.cancelButton).prop('disabled', state);
354 }
354 }
355
355
356 var submitState = state;
356 var submitState = state;
357 if (!submitEvent && this.getCommentStatus() && !this.lineNo) {
357 if (!submitEvent && this.getCommentStatus() && !self.isInline()) {
358 // if the value of commit review status is set, we allow
358 // if the value of commit review status is set, we allow
359 // submit button, but only on Main form, lineNo means inline
359 // submit button, but only on Main form, isInline means inline
360 submitState = false
360 submitState = false
361 }
361 }
362 $(this.submitButton).prop('disabled', submitState);
362 $(this.submitButton).prop('disabled', submitState);
363 if (submitEvent) {
363 if (submitEvent) {
364 $(this.submitButton).val(_gettext('Submitting...'));
364 $(this.submitButton).val(_gettext('Submitting...'));
365 } else {
365 } else {
366 $(this.submitButton).val(this.submitButtonText);
366 $(this.submitButton).val(this.submitButtonText);
367 }
367 }
368
368
369 };
369 };
370
370
371 // lock preview/edit/submit buttons on load, but exclude cancel button
371 // lock preview/edit/submit buttons on load, but exclude cancel button
372 var excludeCancelBtn = true;
372 var excludeCancelBtn = true;
373 this.setActionButtonsDisabled(true, excludeCancelBtn);
373 this.setActionButtonsDisabled(true, excludeCancelBtn);
374
374
375 // anonymous users don't have access to initialized CM instance
375 // anonymous users don't have access to initialized CM instance
376 if (this.cm !== undefined){
376 if (this.cm !== undefined){
377 this.cm.on('change', function(cMirror) {
377 this.cm.on('change', function(cMirror) {
378 if (cMirror.getValue() === "") {
378 if (cMirror.getValue() === "") {
379 self.setActionButtonsDisabled(true, excludeCancelBtn)
379 self.setActionButtonsDisabled(true, excludeCancelBtn)
380 } else {
380 } else {
381 self.setActionButtonsDisabled(false, excludeCancelBtn)
381 self.setActionButtonsDisabled(false, excludeCancelBtn)
382 }
382 }
383 });
383 });
384 }
384 }
385
385
386 $(this.editButton).on('click', function(e) {
386 $(this.editButton).on('click', function(e) {
387 e.preventDefault();
387 e.preventDefault();
388
388
389 $(self.previewButton).parent().removeClass('active');
389 $(self.previewButton).parent().removeClass('active');
390 $(self.previewContainer).hide();
390 $(self.previewContainer).hide();
391
391
392 $(self.editButton).parent().addClass('active');
392 $(self.editButton).parent().addClass('active');
393 $(self.editContainer).show();
393 $(self.editContainer).show();
394
394
395 });
395 });
396
396
397 $(this.previewButton).on('click', function(e) {
397 $(this.previewButton).on('click', function(e) {
398 e.preventDefault();
398 e.preventDefault();
399 var text = self.cm.getValue();
399 var text = self.cm.getValue();
400
400
401 if (text === "") {
401 if (text === "") {
402 return;
402 return;
403 }
403 }
404
404
405 var postData = {
405 var postData = {
406 'text': text,
406 'text': text,
407 'renderer': templateContext.visual.default_renderer,
407 'renderer': templateContext.visual.default_renderer,
408 'csrf_token': CSRF_TOKEN
408 'csrf_token': CSRF_TOKEN
409 };
409 };
410
410
411 // lock ALL buttons on preview
411 // lock ALL buttons on preview
412 self.setActionButtonsDisabled(true);
412 self.setActionButtonsDisabled(true);
413
413
414 $(self.previewBoxSelector).addClass('unloaded');
414 $(self.previewBoxSelector).addClass('unloaded');
415 $(self.previewBoxSelector).html(_gettext('Loading ...'));
415 $(self.previewBoxSelector).html(_gettext('Loading ...'));
416
416
417 $(self.editContainer).hide();
417 $(self.editContainer).hide();
418 $(self.previewContainer).show();
418 $(self.previewContainer).show();
419
419
420 // by default we reset state of comment preserving the text
420 // by default we reset state of comment preserving the text
421 var previewFailCallback = function(){
421 var previewFailCallback = function(){
422 self.resetCommentFormState(text)
422 self.resetCommentFormState(text)
423 };
423 };
424 self.submitAjaxPOST(
424 self.submitAjaxPOST(
425 self.previewUrl, postData, self.previewSuccessCallback,
425 self.previewUrl, postData, self.previewSuccessCallback,
426 previewFailCallback);
426 previewFailCallback);
427
427
428 $(self.previewButton).parent().addClass('active');
428 $(self.previewButton).parent().addClass('active');
429 $(self.editButton).parent().removeClass('active');
429 $(self.editButton).parent().removeClass('active');
430 });
430 });
431
431
432 $(this.submitForm).submit(function(e) {
432 $(this.submitForm).submit(function(e) {
433 e.preventDefault();
433 e.preventDefault();
434 var allowedToSubmit = self.isAllowedToSubmit();
434 var allowedToSubmit = self.isAllowedToSubmit();
435 if (!allowedToSubmit){
435 if (!allowedToSubmit){
436 return false;
436 return false;
437 }
437 }
438 self.handleFormSubmit();
438 self.handleFormSubmit();
439 });
439 });
440
440
441 }
441 }
442
442
443 return CommentForm;
443 return CommentForm;
444 });
444 });
445
445
446 /* comments controller */
446 /* comments controller */
447 var CommentsController = function() {
447 var CommentsController = function() {
448 var mainComment = '#text';
448 var mainComment = '#text';
449 var self = this;
449 var self = this;
450
450
451 this.cancelComment = function(node) {
451 this.cancelComment = function(node) {
452 var $node = $(node);
452 var $node = $(node);
453 var $td = $node.closest('td');
453 var $td = $node.closest('td');
454 $node.closest('.comment-inline-form').remove();
454 $node.closest('.comment-inline-form').remove();
455 return false;
455 return false;
456 };
456 };
457
457
458 this.getLineNumber = function(node) {
458 this.getLineNumber = function(node) {
459 var $node = $(node);
459 var $node = $(node);
460 return $node.closest('td').attr('data-line-number');
460 return $node.closest('td').attr('data-line-number');
461 };
461 };
462
462
463 this.scrollToComment = function(node, offset, outdated) {
463 this.scrollToComment = function(node, offset, outdated) {
464 if (offset === undefined) {
464 if (offset === undefined) {
465 offset = 0;
465 offset = 0;
466 }
466 }
467 var outdated = outdated || false;
467 var outdated = outdated || false;
468 var klass = outdated ? 'div.comment-outdated' : 'div.comment-current';
468 var klass = outdated ? 'div.comment-outdated' : 'div.comment-current';
469
469
470 if (!node) {
470 if (!node) {
471 node = $('.comment-selected');
471 node = $('.comment-selected');
472 if (!node.length) {
472 if (!node.length) {
473 node = $('comment-current')
473 node = $('comment-current')
474 }
474 }
475 }
475 }
476 $wrapper = $(node).closest('div.comment');
476 $wrapper = $(node).closest('div.comment');
477 $comment = $(node).closest(klass);
477 $comment = $(node).closest(klass);
478 $comments = $(klass);
478 $comments = $(klass);
479
479
480 // show hidden comment when referenced.
480 // show hidden comment when referenced.
481 if (!$wrapper.is(':visible')){
481 if (!$wrapper.is(':visible')){
482 $wrapper.show();
482 $wrapper.show();
483 }
483 }
484
484
485 $('.comment-selected').removeClass('comment-selected');
485 $('.comment-selected').removeClass('comment-selected');
486
486
487 var nextIdx = $(klass).index($comment) + offset;
487 var nextIdx = $(klass).index($comment) + offset;
488 if (nextIdx >= $comments.length) {
488 if (nextIdx >= $comments.length) {
489 nextIdx = 0;
489 nextIdx = 0;
490 }
490 }
491 var $next = $(klass).eq(nextIdx);
491 var $next = $(klass).eq(nextIdx);
492
492
493 var $cb = $next.closest('.cb');
493 var $cb = $next.closest('.cb');
494 $cb.removeClass('cb-collapsed');
494 $cb.removeClass('cb-collapsed');
495
495
496 var $filediffCollapseState = $cb.closest('.filediff').prev();
496 var $filediffCollapseState = $cb.closest('.filediff').prev();
497 $filediffCollapseState.prop('checked', false);
497 $filediffCollapseState.prop('checked', false);
498 $next.addClass('comment-selected');
498 $next.addClass('comment-selected');
499 scrollToElement($next);
499 scrollToElement($next);
500 return false;
500 return false;
501 };
501 };
502
502
503 this.nextComment = function(node) {
503 this.nextComment = function(node) {
504 return self.scrollToComment(node, 1);
504 return self.scrollToComment(node, 1);
505 };
505 };
506
506
507 this.prevComment = function(node) {
507 this.prevComment = function(node) {
508 return self.scrollToComment(node, -1);
508 return self.scrollToComment(node, -1);
509 };
509 };
510
510
511 this.nextOutdatedComment = function(node) {
511 this.nextOutdatedComment = function(node) {
512 return self.scrollToComment(node, 1, true);
512 return self.scrollToComment(node, 1, true);
513 };
513 };
514
514
515 this.prevOutdatedComment = function(node) {
515 this.prevOutdatedComment = function(node) {
516 return self.scrollToComment(node, -1, true);
516 return self.scrollToComment(node, -1, true);
517 };
517 };
518
518
519 this.deleteComment = function(node) {
519 this.deleteComment = function(node) {
520 if (!confirm(_gettext('Delete this comment?'))) {
520 if (!confirm(_gettext('Delete this comment?'))) {
521 return false;
521 return false;
522 }
522 }
523 var $node = $(node);
523 var $node = $(node);
524 var $td = $node.closest('td');
524 var $td = $node.closest('td');
525 var $comment = $node.closest('.comment');
525 var $comment = $node.closest('.comment');
526 var comment_id = $comment.attr('data-comment-id');
526 var comment_id = $comment.attr('data-comment-id');
527 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id);
527 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id);
528 var postData = {
528 var postData = {
529 '_method': 'delete',
529 '_method': 'delete',
530 'csrf_token': CSRF_TOKEN
530 'csrf_token': CSRF_TOKEN
531 };
531 };
532
532
533 $comment.addClass('comment-deleting');
533 $comment.addClass('comment-deleting');
534 $comment.hide('fast');
534 $comment.hide('fast');
535
535
536 var success = function(response) {
536 var success = function(response) {
537 $comment.remove();
537 $comment.remove();
538 return false;
538 return false;
539 };
539 };
540 var failure = function(data, textStatus, xhr) {
540 var failure = function(data, textStatus, xhr) {
541 alert("error processing request: " + textStatus);
541 alert("error processing request: " + textStatus);
542 $comment.show('fast');
542 $comment.show('fast');
543 $comment.removeClass('comment-deleting');
543 $comment.removeClass('comment-deleting');
544 return false;
544 return false;
545 };
545 };
546 ajaxPOST(url, postData, success, failure);
546 ajaxPOST(url, postData, success, failure);
547 };
547 };
548
548
549 this.toggleWideMode = function (node) {
549 this.toggleWideMode = function (node) {
550 if ($('#content').hasClass('wrapper')) {
550 if ($('#content').hasClass('wrapper')) {
551 $('#content').removeClass("wrapper");
551 $('#content').removeClass("wrapper");
552 $('#content').addClass("wide-mode-wrapper");
552 $('#content').addClass("wide-mode-wrapper");
553 $(node).addClass('btn-success');
553 $(node).addClass('btn-success');
554 } else {
554 } else {
555 $('#content').removeClass("wide-mode-wrapper");
555 $('#content').removeClass("wide-mode-wrapper");
556 $('#content').addClass("wrapper");
556 $('#content').addClass("wrapper");
557 $(node).removeClass('btn-success');
557 $(node).removeClass('btn-success');
558 }
558 }
559 return false;
559 return false;
560 };
560 };
561
561
562 this.toggleComments = function(node, show) {
562 this.toggleComments = function(node, show) {
563 var $filediff = $(node).closest('.filediff');
563 var $filediff = $(node).closest('.filediff');
564 if (show === true) {
564 if (show === true) {
565 $filediff.removeClass('hide-comments');
565 $filediff.removeClass('hide-comments');
566 } else if (show === false) {
566 } else if (show === false) {
567 $filediff.find('.hide-line-comments').removeClass('hide-line-comments');
567 $filediff.find('.hide-line-comments').removeClass('hide-line-comments');
568 $filediff.addClass('hide-comments');
568 $filediff.addClass('hide-comments');
569 } else {
569 } else {
570 $filediff.find('.hide-line-comments').removeClass('hide-line-comments');
570 $filediff.find('.hide-line-comments').removeClass('hide-line-comments');
571 $filediff.toggleClass('hide-comments');
571 $filediff.toggleClass('hide-comments');
572 }
572 }
573 return false;
573 return false;
574 };
574 };
575
575
576 this.toggleLineComments = function(node) {
576 this.toggleLineComments = function(node) {
577 self.toggleComments(node, true);
577 self.toggleComments(node, true);
578 var $node = $(node);
578 var $node = $(node);
579 $node.closest('tr').toggleClass('hide-line-comments');
579 $node.closest('tr').toggleClass('hide-line-comments');
580 };
580 };
581
581
582 this.createCommentForm = function(formElement, lineno, placeholderText, initAutocompleteActions, resolvesCommentId){
582 this.createCommentForm = function(formElement, lineno, placeholderText, initAutocompleteActions, resolvesCommentId){
583 var pullRequestId = templateContext.pull_request_data.pull_request_id;
583 var pullRequestId = templateContext.pull_request_data.pull_request_id;
584 var commitId = templateContext.commit_data.commit_id;
584 var commitId = templateContext.commit_data.commit_id;
585
585
586 var commentForm = new CommentForm(
586 var commentForm = new CommentForm(
587 formElement, commitId, pullRequestId, lineno, initAutocompleteActions, resolvesCommentId);
587 formElement, commitId, pullRequestId, lineno, initAutocompleteActions, resolvesCommentId);
588 var cm = commentForm.getCmInstance();
588 var cm = commentForm.getCmInstance();
589
589
590 if (resolvesCommentId){
590 if (resolvesCommentId){
591 var placeholderText = _gettext('Leave a comment, or click resolve button to resolve TODO comment #{0}').format(resolvesCommentId);
591 var placeholderText = _gettext('Leave a comment, or click resolve button to resolve TODO comment #{0}').format(resolvesCommentId);
592 }
592 }
593
593
594 setTimeout(function() {
594 setTimeout(function() {
595 // callbacks
595 // callbacks
596 if (cm !== undefined) {
596 if (cm !== undefined) {
597 commentForm.setPlaceholder(placeholderText);
597 commentForm.setPlaceholder(placeholderText);
598 if (commentForm.isInline()) {
598 if (commentForm.isInline()) {
599 cm.focus();
599 cm.focus();
600 cm.refresh();
600 cm.refresh();
601 }
601 }
602 }
602 }
603 }, 10);
603 }, 10);
604
604
605 // trigger scrolldown to the resolve comment, since it might be away
605 // trigger scrolldown to the resolve comment, since it might be away
606 // from the clicked
606 // from the clicked
607 if (resolvesCommentId){
607 if (resolvesCommentId){
608 var actionNode = $(commentForm.resolvesActionId).offset();
608 var actionNode = $(commentForm.resolvesActionId).offset();
609
609
610 setTimeout(function() {
610 setTimeout(function() {
611 if (actionNode) {
611 if (actionNode) {
612 $('body, html').animate({scrollTop: actionNode.top}, 10);
612 $('body, html').animate({scrollTop: actionNode.top}, 10);
613 }
613 }
614 }, 100);
614 }, 100);
615 }
615 }
616
616
617 return commentForm;
617 return commentForm;
618 };
618 };
619
619
620 this.createGeneralComment = function (lineNo, placeholderText, resolvesCommentId) {
620 this.createGeneralComment = function (lineNo, placeholderText, resolvesCommentId) {
621
621
622 var tmpl = $('#cb-comment-general-form-template').html();
622 var tmpl = $('#cb-comment-general-form-template').html();
623 tmpl = tmpl.format(null, 'general');
623 tmpl = tmpl.format(null, 'general');
624 var $form = $(tmpl);
624 var $form = $(tmpl);
625
625
626 var $formPlaceholder = $('#cb-comment-general-form-placeholder');
626 var $formPlaceholder = $('#cb-comment-general-form-placeholder');
627 var curForm = $formPlaceholder.find('form');
627 var curForm = $formPlaceholder.find('form');
628 if (curForm){
628 if (curForm){
629 curForm.remove();
629 curForm.remove();
630 }
630 }
631 $formPlaceholder.append($form);
631 $formPlaceholder.append($form);
632
632
633 var _form = $($form[0]);
633 var _form = $($form[0]);
634 var autocompleteActions = ['approve', 'reject', 'as_note', 'as_todo'];
634 var autocompleteActions = ['approve', 'reject', 'as_note', 'as_todo'];
635 var commentForm = this.createCommentForm(
635 var commentForm = this.createCommentForm(
636 _form, lineNo, placeholderText, autocompleteActions, resolvesCommentId);
636 _form, lineNo, placeholderText, autocompleteActions, resolvesCommentId);
637 commentForm.initStatusChangeSelector();
637 commentForm.initStatusChangeSelector();
638
638
639 return commentForm;
639 return commentForm;
640 };
640 };
641
641
642 this.createComment = function(node, resolutionComment) {
642 this.createComment = function(node, resolutionComment) {
643 var resolvesCommentId = resolutionComment || null;
643 var resolvesCommentId = resolutionComment || null;
644 var $node = $(node);
644 var $node = $(node);
645 var $td = $node.closest('td');
645 var $td = $node.closest('td');
646 var $form = $td.find('.comment-inline-form');
646 var $form = $td.find('.comment-inline-form');
647
647
648 if (!$form.length) {
648 if (!$form.length) {
649
649
650 var $filediff = $node.closest('.filediff');
650 var $filediff = $node.closest('.filediff');
651 $filediff.removeClass('hide-comments');
651 $filediff.removeClass('hide-comments');
652 var f_path = $filediff.attr('data-f-path');
652 var f_path = $filediff.attr('data-f-path');
653 var lineno = self.getLineNumber(node);
653 var lineno = self.getLineNumber(node);
654 // create a new HTML from template
654 // create a new HTML from template
655 var tmpl = $('#cb-comment-inline-form-template').html();
655 var tmpl = $('#cb-comment-inline-form-template').html();
656 tmpl = tmpl.format(f_path, lineno);
656 tmpl = tmpl.format(f_path, lineno);
657 $form = $(tmpl);
657 $form = $(tmpl);
658
658
659 var $comments = $td.find('.inline-comments');
659 var $comments = $td.find('.inline-comments');
660 if (!$comments.length) {
660 if (!$comments.length) {
661 $comments = $(
661 $comments = $(
662 $('#cb-comments-inline-container-template').html());
662 $('#cb-comments-inline-container-template').html());
663 $td.append($comments);
663 $td.append($comments);
664 }
664 }
665
665
666 $td.find('.cb-comment-add-button').before($form);
666 $td.find('.cb-comment-add-button').before($form);
667
667
668 var placeholderText = _gettext('Leave a comment on line {0}.').format(lineno);
668 var placeholderText = _gettext('Leave a comment on line {0}.').format(lineno);
669 var _form = $($form[0]).find('form');
669 var _form = $($form[0]).find('form');
670 var autocompleteActions = ['as_note', 'as_todo'];
670 var autocompleteActions = ['as_note', 'as_todo'];
671 var commentForm = this.createCommentForm(
671 var commentForm = this.createCommentForm(
672 _form, lineno, placeholderText, autocompleteActions, resolvesCommentId);
672 _form, lineno, placeholderText, autocompleteActions, resolvesCommentId);
673
673
674 $.Topic('/ui/plugins/code/comment_form_built').prepareOrPublish({
674 $.Topic('/ui/plugins/code/comment_form_built').prepareOrPublish({
675 form: _form,
675 form: _form,
676 parent: $td[0],
676 parent: $td[0],
677 lineno: lineno,
677 lineno: lineno,
678 f_path: f_path}
678 f_path: f_path}
679 );
679 );
680
680
681 // set a CUSTOM submit handler for inline comments.
681 // set a CUSTOM submit handler for inline comments.
682 commentForm.setHandleFormSubmit(function(o) {
682 commentForm.setHandleFormSubmit(function(o) {
683 var text = commentForm.cm.getValue();
683 var text = commentForm.cm.getValue();
684 var commentType = commentForm.getCommentType();
684 var commentType = commentForm.getCommentType();
685 var resolvesCommentId = commentForm.getResolvesId();
685 var resolvesCommentId = commentForm.getResolvesId();
686
686
687 if (text === "") {
687 if (text === "") {
688 return;
688 return;
689 }
689 }
690
690
691 if (lineno === undefined) {
691 if (lineno === undefined) {
692 alert('missing line !');
692 alert('missing line !');
693 return;
693 return;
694 }
694 }
695 if (f_path === undefined) {
695 if (f_path === undefined) {
696 alert('missing file path !');
696 alert('missing file path !');
697 return;
697 return;
698 }
698 }
699
699
700 var excludeCancelBtn = false;
700 var excludeCancelBtn = false;
701 var submitEvent = true;
701 var submitEvent = true;
702 commentForm.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
702 commentForm.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
703 commentForm.cm.setOption("readOnly", true);
703 commentForm.cm.setOption("readOnly", true);
704 var postData = {
704 var postData = {
705 'text': text,
705 'text': text,
706 'f_path': f_path,
706 'f_path': f_path,
707 'line': lineno,
707 'line': lineno,
708 'comment_type': commentType,
708 'comment_type': commentType,
709 'csrf_token': CSRF_TOKEN
709 'csrf_token': CSRF_TOKEN
710 };
710 };
711 if (resolvesCommentId){
711 if (resolvesCommentId){
712 postData['resolves_comment_id'] = resolvesCommentId;
712 postData['resolves_comment_id'] = resolvesCommentId;
713 }
713 }
714
714
715 var submitSuccessCallback = function(json_data) {
715 var submitSuccessCallback = function(json_data) {
716 $form.remove();
716 $form.remove();
717 try {
717 try {
718 var html = json_data.rendered_text;
718 var html = json_data.rendered_text;
719 var lineno = json_data.line_no;
719 var lineno = json_data.line_no;
720 var target_id = json_data.target_id;
720 var target_id = json_data.target_id;
721
721
722 $comments.find('.cb-comment-add-button').before(html);
722 $comments.find('.cb-comment-add-button').before(html);
723
723
724 //mark visually which comment was resolved
724 //mark visually which comment was resolved
725 if (resolvesCommentId) {
725 if (resolvesCommentId) {
726 commentForm.markCommentResolved(resolvesCommentId);
726 commentForm.markCommentResolved(resolvesCommentId);
727 }
727 }
728
728
729 // run global callback on submit
729 // run global callback on submit
730 commentForm.globalSubmitSuccessCallback();
730 commentForm.globalSubmitSuccessCallback();
731
731
732 } catch (e) {
732 } catch (e) {
733 console.error(e);
733 console.error(e);
734 }
734 }
735
735
736 // re trigger the linkification of next/prev navigation
736 // re trigger the linkification of next/prev navigation
737 linkifyComments($('.inline-comment-injected'));
737 linkifyComments($('.inline-comment-injected'));
738 timeagoActivate();
738 timeagoActivate();
739 commentForm.setActionButtonsDisabled(false);
739 commentForm.setActionButtonsDisabled(false);
740
740
741 };
741 };
742 var submitFailCallback = function(){
742 var submitFailCallback = function(){
743 commentForm.resetCommentFormState(text)
743 commentForm.resetCommentFormState(text)
744 };
744 };
745 commentForm.submitAjaxPOST(
745 commentForm.submitAjaxPOST(
746 commentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback);
746 commentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback);
747 });
747 });
748 }
748 }
749
749
750 $form.addClass('comment-inline-form-open');
750 $form.addClass('comment-inline-form-open');
751 };
751 };
752
752
753 this.createResolutionComment = function(commentId){
753 this.createResolutionComment = function(commentId){
754 // hide the trigger text
754 // hide the trigger text
755 $('#resolve-comment-{0}'.format(commentId)).hide();
755 $('#resolve-comment-{0}'.format(commentId)).hide();
756
756
757 var comment = $('#comment-'+commentId);
757 var comment = $('#comment-'+commentId);
758 var commentData = comment.data();
758 var commentData = comment.data();
759 if (commentData.commentInline) {
759 if (commentData.commentInline) {
760 this.createComment(comment, commentId)
760 this.createComment(comment, commentId)
761 } else {
761 } else {
762 Rhodecode.comments.createGeneralComment('general', "$placeholder", commentId)
762 Rhodecode.comments.createGeneralComment('general', "$placeholder", commentId)
763 }
763 }
764
764
765 return false;
765 return false;
766 };
766 };
767
767
768 this.submitResolution = function(commentId){
768 this.submitResolution = function(commentId){
769 var form = $('#resolve_comment_{0}'.format(commentId)).closest('form');
769 var form = $('#resolve_comment_{0}'.format(commentId)).closest('form');
770 var commentForm = form.get(0).CommentForm;
770 var commentForm = form.get(0).CommentForm;
771
771
772 var cm = commentForm.getCmInstance();
772 var cm = commentForm.getCmInstance();
773 var renderer = templateContext.visual.default_renderer;
773 var renderer = templateContext.visual.default_renderer;
774 if (renderer == 'rst'){
774 if (renderer == 'rst'){
775 var commentUrl = '`#{0} <{1}#comment-{0}>`_'.format(commentId, commentForm.selfUrl);
775 var commentUrl = '`#{0} <{1}#comment-{0}>`_'.format(commentId, commentForm.selfUrl);
776 } else if (renderer == 'markdown') {
776 } else if (renderer == 'markdown') {
777 var commentUrl = '[#{0}]({1}#comment-{0})'.format(commentId, commentForm.selfUrl);
777 var commentUrl = '[#{0}]({1}#comment-{0})'.format(commentId, commentForm.selfUrl);
778 } else {
778 } else {
779 var commentUrl = '{1}#comment-{0}'.format(commentId, commentForm.selfUrl);
779 var commentUrl = '{1}#comment-{0}'.format(commentId, commentForm.selfUrl);
780 }
780 }
781
781
782 cm.setValue(_gettext('TODO from comment {0} was fixed.').format(commentUrl));
782 cm.setValue(_gettext('TODO from comment {0} was fixed.').format(commentUrl));
783 form.submit();
783 form.submit();
784 return false;
784 return false;
785 };
785 };
786
786
787 this.renderInlineComments = function(file_comments) {
787 this.renderInlineComments = function(file_comments) {
788 show_add_button = typeof show_add_button !== 'undefined' ? show_add_button : true;
788 show_add_button = typeof show_add_button !== 'undefined' ? show_add_button : true;
789
789
790 for (var i = 0; i < file_comments.length; i++) {
790 for (var i = 0; i < file_comments.length; i++) {
791 var box = file_comments[i];
791 var box = file_comments[i];
792
792
793 var target_id = $(box).attr('target_id');
793 var target_id = $(box).attr('target_id');
794
794
795 // actually comments with line numbers
795 // actually comments with line numbers
796 var comments = box.children;
796 var comments = box.children;
797
797
798 for (var j = 0; j < comments.length; j++) {
798 for (var j = 0; j < comments.length; j++) {
799 var data = {
799 var data = {
800 'rendered_text': comments[j].outerHTML,
800 'rendered_text': comments[j].outerHTML,
801 'line_no': $(comments[j]).attr('line'),
801 'line_no': $(comments[j]).attr('line'),
802 'target_id': target_id
802 'target_id': target_id
803 };
803 };
804 }
804 }
805 }
805 }
806
806
807 // since order of injection is random, we're now re-iterating
807 // since order of injection is random, we're now re-iterating
808 // from correct order and filling in links
808 // from correct order and filling in links
809 linkifyComments($('.inline-comment-injected'));
809 linkifyComments($('.inline-comment-injected'));
810 firefoxAnchorFix();
810 firefoxAnchorFix();
811 };
811 };
812
812
813 };
813 };
General Comments 0
You need to be logged in to leave comments. Login now