##// END OF EJS Templates
comments: send more information with comment_form_built event
ergo -
r453:f10fca48 default
parent child Browse files
Show More
@@ -1,653 +1,655 b''
1 // # Copyright (C) 2010-2016 RhodeCode GmbH
1 // # Copyright (C) 2010-2016 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 // returns a node from given html;
28 // returns a node from given html;
29 var fromHTML = function(html){
29 var fromHTML = function(html){
30 var _html = document.createElement('element');
30 var _html = document.createElement('element');
31 _html.innerHTML = html;
31 _html.innerHTML = html;
32 return _html;
32 return _html;
33 };
33 };
34
34
35 var tableTr = function(cls, body){
35 var tableTr = function(cls, body){
36 var _el = document.createElement('div');
36 var _el = document.createElement('div');
37 var _body = $(body).attr('id');
37 var _body = $(body).attr('id');
38 var comment_id = fromHTML(body).children[0].id.split('comment-')[1];
38 var comment_id = fromHTML(body).children[0].id.split('comment-')[1];
39 var id = 'comment-tr-{0}'.format(comment_id);
39 var id = 'comment-tr-{0}'.format(comment_id);
40 var _html = ('<table><tbody><tr id="{0}" class="{1}">'+
40 var _html = ('<table><tbody><tr id="{0}" class="{1}">'+
41 '<td class="add-comment-line"><span class="add-comment-content"></span></td>'+
41 '<td class="add-comment-line"><span class="add-comment-content"></span></td>'+
42 '<td></td>'+
42 '<td></td>'+
43 '<td></td>'+
43 '<td></td>'+
44 '<td>{2}</td>'+
44 '<td>{2}</td>'+
45 '</tr></tbody></table>').format(id, cls, body);
45 '</tr></tbody></table>').format(id, cls, body);
46 $(_el).html(_html);
46 $(_el).html(_html);
47 return _el.children[0].children[0].children[0];
47 return _el.children[0].children[0].children[0];
48 };
48 };
49
49
50 var removeInlineForm = function(form) {
50 var removeInlineForm = function(form) {
51 form.parentNode.removeChild(form);
51 form.parentNode.removeChild(form);
52 };
52 };
53
53
54 var createInlineForm = function(parent_tr, f_path, line) {
54 var createInlineForm = function(parent_tr, f_path, line) {
55 var tmpl = $('#comment-inline-form-template').html();
55 var tmpl = $('#comment-inline-form-template').html();
56 tmpl = tmpl.format(f_path, line);
56 tmpl = tmpl.format(f_path, line);
57 var form = tableTr('comment-form-inline', tmpl);
57 var form = tableTr('comment-form-inline', tmpl);
58 var form_hide_button = $(form).find('.hide-inline-form');
58 var form_hide_button = $(form).find('.hide-inline-form');
59
59
60 $(form_hide_button).click(function(e) {
60 $(form_hide_button).click(function(e) {
61 $('.inline-comments').removeClass('hide-comment-button');
61 $('.inline-comments').removeClass('hide-comment-button');
62 var newtr = e.currentTarget.parentNode.parentNode.parentNode.parentNode.parentNode;
62 var newtr = e.currentTarget.parentNode.parentNode.parentNode.parentNode.parentNode;
63 if ($(newtr.nextElementSibling).hasClass('inline-comments-button')) {
63 if ($(newtr.nextElementSibling).hasClass('inline-comments-button')) {
64 $(newtr.nextElementSibling).show();
64 $(newtr.nextElementSibling).show();
65 }
65 }
66 $(newtr).parents('.comment-form-inline').remove();
66 $(newtr).parents('.comment-form-inline').remove();
67 $(parent_tr).removeClass('form-open');
67 $(parent_tr).removeClass('form-open');
68 $(parent_tr).removeClass('hl-comment');
68 $(parent_tr).removeClass('hl-comment');
69 });
69 });
70
70
71 return form;
71 return form;
72 };
72 };
73
73
74 var getLineNo = function(tr) {
74 var getLineNo = function(tr) {
75 var line;
75 var line;
76 // Try to get the id and return "" (empty string) if it doesn't exist
76 // Try to get the id and return "" (empty string) if it doesn't exist
77 var o = ($(tr).find('.lineno.old').attr('id')||"").split('_');
77 var o = ($(tr).find('.lineno.old').attr('id')||"").split('_');
78 var n = ($(tr).find('.lineno.new').attr('id')||"").split('_');
78 var n = ($(tr).find('.lineno.new').attr('id')||"").split('_');
79 if (n.length >= 2) {
79 if (n.length >= 2) {
80 line = n[n.length-1];
80 line = n[n.length-1];
81 } else if (o.length >= 2) {
81 } else if (o.length >= 2) {
82 line = o[o.length-1];
82 line = o[o.length-1];
83 }
83 }
84 return line;
84 return line;
85 };
85 };
86
86
87 /**
87 /**
88 * make a single inline comment and place it inside
88 * make a single inline comment and place it inside
89 */
89 */
90 var renderInlineComment = function(json_data, show_add_button) {
90 var renderInlineComment = function(json_data, show_add_button) {
91 show_add_button = typeof show_add_button !== 'undefined' ? show_add_button : true;
91 show_add_button = typeof show_add_button !== 'undefined' ? show_add_button : true;
92 try {
92 try {
93 var html = json_data.rendered_text;
93 var html = json_data.rendered_text;
94 var lineno = json_data.line_no;
94 var lineno = json_data.line_no;
95 var target_id = json_data.target_id;
95 var target_id = json_data.target_id;
96 placeInline(target_id, lineno, html, show_add_button);
96 placeInline(target_id, lineno, html, show_add_button);
97 } catch (e) {
97 } catch (e) {
98 console.error(e);
98 console.error(e);
99 }
99 }
100 };
100 };
101
101
102 function bindDeleteCommentButtons() {
102 function bindDeleteCommentButtons() {
103 $('.delete-comment').one('click', function() {
103 $('.delete-comment').one('click', function() {
104 var comment_id = $(this).data("comment-id");
104 var comment_id = $(this).data("comment-id");
105
105
106 if (comment_id){
106 if (comment_id){
107 deleteComment(comment_id);
107 deleteComment(comment_id);
108 }
108 }
109 });
109 });
110 }
110 }
111
111
112 /**
112 /**
113 * Inject inline comment for on given TR this tr should be always an .line
113 * Inject inline comment for on given TR this tr should be always an .line
114 * tr containing the line. Code will detect comment, and always put the comment
114 * tr containing the line. Code will detect comment, and always put the comment
115 * block at the very bottom
115 * block at the very bottom
116 */
116 */
117 var injectInlineForm = function(tr){
117 var injectInlineForm = function(tr){
118 if (!$(tr).hasClass('line')) {
118 if (!$(tr).hasClass('line')) {
119 return;
119 return;
120 }
120 }
121
121
122 var _td = $(tr).find('.code').get(0);
122 var _td = $(tr).find('.code').get(0);
123 if ($(tr).hasClass('form-open') ||
123 if ($(tr).hasClass('form-open') ||
124 $(tr).hasClass('context') ||
124 $(tr).hasClass('context') ||
125 $(_td).hasClass('no-comment')) {
125 $(_td).hasClass('no-comment')) {
126 return;
126 return;
127 }
127 }
128 $(tr).addClass('form-open');
128 $(tr).addClass('form-open');
129 $(tr).addClass('hl-comment');
129 $(tr).addClass('hl-comment');
130 var node = $(tr.parentNode.parentNode.parentNode).find('.full_f_path').get(0);
130 var node = $(tr.parentNode.parentNode.parentNode).find('.full_f_path').get(0);
131 var f_path = $(node).attr('path');
131 var f_path = $(node).attr('path');
132 var lineno = getLineNo(tr);
132 var lineno = getLineNo(tr);
133 var form = createInlineForm(tr, f_path, lineno);
133 var form = createInlineForm(tr, f_path, lineno);
134
134
135 var parent = tr;
135 var parent = tr;
136 while (1) {
136 while (1) {
137 var n = parent.nextElementSibling;
137 var n = parent.nextElementSibling;
138 // next element are comments !
138 // next element are comments !
139 if ($(n).hasClass('inline-comments')) {
139 if ($(n).hasClass('inline-comments')) {
140 parent = n;
140 parent = n;
141 }
141 }
142 else {
142 else {
143 break;
143 break;
144 }
144 }
145 }
145 }
146 var _parent = $(parent).get(0);
146 var _parent = $(parent).get(0);
147 $(_parent).after(form);
147 $(_parent).after(form);
148 $('.comment-form-inline').prev('.inline-comments').addClass('hide-comment-button');
148 $('.comment-form-inline').prev('.inline-comments').addClass('hide-comment-button');
149 var f = $(form).get(0);
149 var f = $(form).get(0);
150
150
151 var _form = $(f).find('.inline-form').get(0);
151 var _form = $(f).find('.inline-form').get(0);
152
152
153 var pullRequestId = templateContext.pull_request_data.pull_request_id;
153 var pullRequestId = templateContext.pull_request_data.pull_request_id;
154 var commitId = templateContext.commit_data.commit_id;
154 var commitId = templateContext.commit_data.commit_id;
155
155
156 var commentForm = new CommentForm(_form, commitId, pullRequestId, lineno, false);
156 var commentForm = new CommentForm(_form, commitId, pullRequestId, lineno, false);
157 var cm = commentForm.getCmInstance();
157 var cm = commentForm.getCmInstance();
158
158
159 // set a CUSTOM submit handler for inline comments.
159 // set a CUSTOM submit handler for inline comments.
160 commentForm.setHandleFormSubmit(function(o) {
160 commentForm.setHandleFormSubmit(function(o) {
161 var text = commentForm.cm.getValue();
161 var text = commentForm.cm.getValue();
162
162
163 if (text === "") {
163 if (text === "") {
164 return;
164 return;
165 }
165 }
166
166
167 if (lineno === undefined) {
167 if (lineno === undefined) {
168 alert('missing line !');
168 alert('missing line !');
169 return;
169 return;
170 }
170 }
171 if (f_path === undefined) {
171 if (f_path === undefined) {
172 alert('missing file path !');
172 alert('missing file path !');
173 return;
173 return;
174 }
174 }
175
175
176 var excludeCancelBtn = false;
176 var excludeCancelBtn = false;
177 var submitEvent = true;
177 var submitEvent = true;
178 commentForm.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
178 commentForm.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
179 commentForm.cm.setOption("readOnly", true);
179 commentForm.cm.setOption("readOnly", true);
180 var postData = {
180 var postData = {
181 'text': text,
181 'text': text,
182 'f_path': f_path,
182 'f_path': f_path,
183 'line': lineno,
183 'line': lineno,
184 'csrf_token': CSRF_TOKEN
184 'csrf_token': CSRF_TOKEN
185 };
185 };
186 var submitSuccessCallback = function(o) {
186 var submitSuccessCallback = function(o) {
187 $(tr).removeClass('form-open');
187 $(tr).removeClass('form-open');
188 removeInlineForm(f);
188 removeInlineForm(f);
189 renderInlineComment(o);
189 renderInlineComment(o);
190 $('.inline-comments').removeClass('hide-comment-button');
190 $('.inline-comments').removeClass('hide-comment-button');
191
191
192 // re trigger the linkification of next/prev navigation
192 // re trigger the linkification of next/prev navigation
193 linkifyComments($('.inline-comment-injected'));
193 linkifyComments($('.inline-comment-injected'));
194 timeagoActivate();
194 timeagoActivate();
195 bindDeleteCommentButtons();
195 bindDeleteCommentButtons();
196 commentForm.setActionButtonsDisabled(false);
196 commentForm.setActionButtonsDisabled(false);
197
197
198 };
198 };
199 var submitFailCallback = function(){
199 var submitFailCallback = function(){
200 commentForm.resetCommentFormState(text)
200 commentForm.resetCommentFormState(text)
201 };
201 };
202 commentForm.submitAjaxPOST(
202 commentForm.submitAjaxPOST(
203 commentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback);
203 commentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback);
204 });
204 });
205
205
206 setTimeout(function() {
206 setTimeout(function() {
207 // callbacks
207 // callbacks
208 if (cm !== undefined) {
208 if (cm !== undefined) {
209 cm.focus();
209 cm.focus();
210 }
210 }
211 }, 10);
211 }, 10);
212
212
213 $.Topic('/ui/plugins/code/comment_form_built').prepareOrPublish({
213 $.Topic('/ui/plugins/code/comment_form_built').prepareOrPublish({
214 form:_form,
214 form:_form,
215 parent:_parent}
215 parent:_parent,
216 lineno: lineno,
217 f_path: f_path}
216 );
218 );
217 };
219 };
218
220
219 var deleteComment = function(comment_id) {
221 var deleteComment = function(comment_id) {
220 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id);
222 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id);
221 var postData = {
223 var postData = {
222 '_method': 'delete',
224 '_method': 'delete',
223 'csrf_token': CSRF_TOKEN
225 'csrf_token': CSRF_TOKEN
224 };
226 };
225
227
226 var success = function(o) {
228 var success = function(o) {
227 window.location.reload();
229 window.location.reload();
228 };
230 };
229 ajaxPOST(url, postData, success);
231 ajaxPOST(url, postData, success);
230 };
232 };
231
233
232 var createInlineAddButton = function(tr){
234 var createInlineAddButton = function(tr){
233 var label = _gettext('Add another comment');
235 var label = _gettext('Add another comment');
234 var html_el = document.createElement('div');
236 var html_el = document.createElement('div');
235 $(html_el).addClass('add-comment');
237 $(html_el).addClass('add-comment');
236 html_el.innerHTML = '<span class="btn btn-secondary">{0}</span>'.format(label);
238 html_el.innerHTML = '<span class="btn btn-secondary">{0}</span>'.format(label);
237 var add = new $(html_el);
239 var add = new $(html_el);
238 add.on('click', function(e) {
240 add.on('click', function(e) {
239 injectInlineForm(tr);
241 injectInlineForm(tr);
240 });
242 });
241 return add;
243 return add;
242 };
244 };
243
245
244 var placeAddButton = function(target_tr){
246 var placeAddButton = function(target_tr){
245 if(!target_tr){
247 if(!target_tr){
246 return;
248 return;
247 }
249 }
248 var last_node = target_tr;
250 var last_node = target_tr;
249 // scan
251 // scan
250 while (1){
252 while (1){
251 var n = last_node.nextElementSibling;
253 var n = last_node.nextElementSibling;
252 // next element are comments !
254 // next element are comments !
253 if($(n).hasClass('inline-comments')){
255 if($(n).hasClass('inline-comments')){
254 last_node = n;
256 last_node = n;
255 // also remove the comment button from previous
257 // also remove the comment button from previous
256 var comment_add_buttons = $(last_node).find('.add-comment');
258 var comment_add_buttons = $(last_node).find('.add-comment');
257 for(var i=0; i<comment_add_buttons.length; i++){
259 for(var i=0; i<comment_add_buttons.length; i++){
258 var b = comment_add_buttons[i];
260 var b = comment_add_buttons[i];
259 b.parentNode.removeChild(b);
261 b.parentNode.removeChild(b);
260 }
262 }
261 }
263 }
262 else{
264 else{
263 break;
265 break;
264 }
266 }
265 }
267 }
266 var add = createInlineAddButton(target_tr);
268 var add = createInlineAddButton(target_tr);
267 // get the comment div
269 // get the comment div
268 var comment_block = $(last_node).find('.comment')[0];
270 var comment_block = $(last_node).find('.comment')[0];
269 // attach add button
271 // attach add button
270 $(add).insertAfter(comment_block);
272 $(add).insertAfter(comment_block);
271 };
273 };
272
274
273 /**
275 /**
274 * Places the inline comment into the changeset block in proper line position
276 * Places the inline comment into the changeset block in proper line position
275 */
277 */
276 var placeInline = function(target_container, lineno, html, show_add_button) {
278 var placeInline = function(target_container, lineno, html, show_add_button) {
277 show_add_button = typeof show_add_button !== 'undefined' ? show_add_button : true;
279 show_add_button = typeof show_add_button !== 'undefined' ? show_add_button : true;
278
280
279 var lineid = "{0}_{1}".format(target_container, lineno);
281 var lineid = "{0}_{1}".format(target_container, lineno);
280 var target_line = $('#' + lineid).get(0);
282 var target_line = $('#' + lineid).get(0);
281 var comment = new $(tableTr('inline-comments', html));
283 var comment = new $(tableTr('inline-comments', html));
282 // check if there are comments already !
284 // check if there are comments already !
283 var parent_node = target_line.parentNode;
285 var parent_node = target_line.parentNode;
284 var root_parent = parent_node;
286 var root_parent = parent_node;
285 while (1) {
287 while (1) {
286 var n = parent_node.nextElementSibling;
288 var n = parent_node.nextElementSibling;
287 // next element are comments !
289 // next element are comments !
288 if ($(n).hasClass('inline-comments')) {
290 if ($(n).hasClass('inline-comments')) {
289 parent_node = n;
291 parent_node = n;
290 }
292 }
291 else {
293 else {
292 break;
294 break;
293 }
295 }
294 }
296 }
295 // put in the comment at the bottom
297 // put in the comment at the bottom
296 $(comment).insertAfter(parent_node);
298 $(comment).insertAfter(parent_node);
297 $(comment).find('.comment-inline').addClass('inline-comment-injected');
299 $(comment).find('.comment-inline').addClass('inline-comment-injected');
298 // scan nodes, and attach add button to last one
300 // scan nodes, and attach add button to last one
299 if (show_add_button) {
301 if (show_add_button) {
300 placeAddButton(root_parent);
302 placeAddButton(root_parent);
301 }
303 }
302
304
303 return target_line;
305 return target_line;
304 };
306 };
305
307
306 var linkifyComments = function(comments) {
308 var linkifyComments = function(comments) {
307
309
308 for (var i = 0; i < comments.length; i++) {
310 for (var i = 0; i < comments.length; i++) {
309 var comment_id = $(comments[i]).data('comment-id');
311 var comment_id = $(comments[i]).data('comment-id');
310 var prev_comment_id = $(comments[i - 1]).data('comment-id');
312 var prev_comment_id = $(comments[i - 1]).data('comment-id');
311 var next_comment_id = $(comments[i + 1]).data('comment-id');
313 var next_comment_id = $(comments[i + 1]).data('comment-id');
312
314
313 // place next/prev links
315 // place next/prev links
314 if (prev_comment_id) {
316 if (prev_comment_id) {
315 $('#prev_c_' + comment_id).show();
317 $('#prev_c_' + comment_id).show();
316 $('#prev_c_' + comment_id + " a.arrow_comment_link").attr(
318 $('#prev_c_' + comment_id + " a.arrow_comment_link").attr(
317 'href', '#comment-' + prev_comment_id).removeClass('disabled');
319 'href', '#comment-' + prev_comment_id).removeClass('disabled');
318 }
320 }
319 if (next_comment_id) {
321 if (next_comment_id) {
320 $('#next_c_' + comment_id).show();
322 $('#next_c_' + comment_id).show();
321 $('#next_c_' + comment_id + " a.arrow_comment_link").attr(
323 $('#next_c_' + comment_id + " a.arrow_comment_link").attr(
322 'href', '#comment-' + next_comment_id).removeClass('disabled');
324 'href', '#comment-' + next_comment_id).removeClass('disabled');
323 }
325 }
324 // place a first link to the total counter
326 // place a first link to the total counter
325 if (i === 0) {
327 if (i === 0) {
326 $('#inline-comments-counter').attr('href', '#comment-' + comment_id);
328 $('#inline-comments-counter').attr('href', '#comment-' + comment_id);
327 }
329 }
328 }
330 }
329
331
330 };
332 };
331
333
332 /**
334 /**
333 * Iterates over all the inlines, and places them inside proper blocks of data
335 * Iterates over all the inlines, and places them inside proper blocks of data
334 */
336 */
335 var renderInlineComments = function(file_comments, show_add_button) {
337 var renderInlineComments = function(file_comments, show_add_button) {
336 show_add_button = typeof show_add_button !== 'undefined' ? show_add_button : true;
338 show_add_button = typeof show_add_button !== 'undefined' ? show_add_button : true;
337
339
338 for (var i = 0; i < file_comments.length; i++) {
340 for (var i = 0; i < file_comments.length; i++) {
339 var box = file_comments[i];
341 var box = file_comments[i];
340
342
341 var target_id = $(box).attr('target_id');
343 var target_id = $(box).attr('target_id');
342
344
343 // actually comments with line numbers
345 // actually comments with line numbers
344 var comments = box.children;
346 var comments = box.children;
345
347
346 for (var j = 0; j < comments.length; j++) {
348 for (var j = 0; j < comments.length; j++) {
347 var data = {
349 var data = {
348 'rendered_text': comments[j].outerHTML,
350 'rendered_text': comments[j].outerHTML,
349 'line_no': $(comments[j]).attr('line'),
351 'line_no': $(comments[j]).attr('line'),
350 'target_id': target_id
352 'target_id': target_id
351 };
353 };
352 renderInlineComment(data, show_add_button);
354 renderInlineComment(data, show_add_button);
353 }
355 }
354 }
356 }
355
357
356 // since order of injection is random, we're now re-iterating
358 // since order of injection is random, we're now re-iterating
357 // from correct order and filling in links
359 // from correct order and filling in links
358 linkifyComments($('.inline-comment-injected'));
360 linkifyComments($('.inline-comment-injected'));
359 bindDeleteCommentButtons();
361 bindDeleteCommentButtons();
360 firefoxAnchorFix();
362 firefoxAnchorFix();
361 };
363 };
362
364
363
365
364 /* Comment form for main and inline comments */
366 /* Comment form for main and inline comments */
365 var CommentForm = (function() {
367 var CommentForm = (function() {
366 "use strict";
368 "use strict";
367
369
368 function CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions) {
370 function CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions) {
369
371
370 this.withLineNo = function(selector) {
372 this.withLineNo = function(selector) {
371 var lineNo = this.lineNo;
373 var lineNo = this.lineNo;
372 if (lineNo === undefined) {
374 if (lineNo === undefined) {
373 return selector
375 return selector
374 } else {
376 } else {
375 return selector + '_' + lineNo;
377 return selector + '_' + lineNo;
376 }
378 }
377 };
379 };
378
380
379 this.commitId = commitId;
381 this.commitId = commitId;
380 this.pullRequestId = pullRequestId;
382 this.pullRequestId = pullRequestId;
381 this.lineNo = lineNo;
383 this.lineNo = lineNo;
382 this.initAutocompleteActions = initAutocompleteActions;
384 this.initAutocompleteActions = initAutocompleteActions;
383
385
384 this.previewButton = this.withLineNo('#preview-btn');
386 this.previewButton = this.withLineNo('#preview-btn');
385 this.previewContainer = this.withLineNo('#preview-container');
387 this.previewContainer = this.withLineNo('#preview-container');
386
388
387 this.previewBoxSelector = this.withLineNo('#preview-box');
389 this.previewBoxSelector = this.withLineNo('#preview-box');
388
390
389 this.editButton = this.withLineNo('#edit-btn');
391 this.editButton = this.withLineNo('#edit-btn');
390 this.editContainer = this.withLineNo('#edit-container');
392 this.editContainer = this.withLineNo('#edit-container');
391
393
392 this.cancelButton = this.withLineNo('#cancel-btn');
394 this.cancelButton = this.withLineNo('#cancel-btn');
393
395
394 this.statusChange = '#change_status';
396 this.statusChange = '#change_status';
395 this.cmBox = this.withLineNo('#text');
397 this.cmBox = this.withLineNo('#text');
396 this.cm = initCommentBoxCodeMirror(this.cmBox, this.initAutocompleteActions);
398 this.cm = initCommentBoxCodeMirror(this.cmBox, this.initAutocompleteActions);
397
399
398 this.submitForm = formElement;
400 this.submitForm = formElement;
399 this.submitButton = $(this.submitForm).find('input[type="submit"]');
401 this.submitButton = $(this.submitForm).find('input[type="submit"]');
400 this.submitButtonText = this.submitButton.val();
402 this.submitButtonText = this.submitButton.val();
401
403
402 this.previewUrl = pyroutes.url('changeset_comment_preview',
404 this.previewUrl = pyroutes.url('changeset_comment_preview',
403 {'repo_name': templateContext.repo_name});
405 {'repo_name': templateContext.repo_name});
404
406
405 // based on commitId, or pullReuqestId decide where do we submit
407 // based on commitId, or pullReuqestId decide where do we submit
406 // out data
408 // out data
407 if (this.commitId){
409 if (this.commitId){
408 this.submitUrl = pyroutes.url('changeset_comment',
410 this.submitUrl = pyroutes.url('changeset_comment',
409 {'repo_name': templateContext.repo_name,
411 {'repo_name': templateContext.repo_name,
410 'revision': this.commitId});
412 'revision': this.commitId});
411
413
412 } else if (this.pullRequestId) {
414 } else if (this.pullRequestId) {
413 this.submitUrl = pyroutes.url('pullrequest_comment',
415 this.submitUrl = pyroutes.url('pullrequest_comment',
414 {'repo_name': templateContext.repo_name,
416 {'repo_name': templateContext.repo_name,
415 'pull_request_id': this.pullRequestId});
417 'pull_request_id': this.pullRequestId});
416
418
417 } else {
419 } else {
418 throw new Error(
420 throw new Error(
419 'CommentForm requires pullRequestId, or commitId to be specified.')
421 'CommentForm requires pullRequestId, or commitId to be specified.')
420 }
422 }
421
423
422 this.getCmInstance = function(){
424 this.getCmInstance = function(){
423 return this.cm
425 return this.cm
424 };
426 };
425
427
426 var self = this;
428 var self = this;
427
429
428 this.getCommentStatus = function() {
430 this.getCommentStatus = function() {
429 return $(this.submitForm).find(this.statusChange).val();
431 return $(this.submitForm).find(this.statusChange).val();
430 };
432 };
431
433
432 this.isAllowedToSubmit = function() {
434 this.isAllowedToSubmit = function() {
433 return !$(this.submitButton).prop('disabled');
435 return !$(this.submitButton).prop('disabled');
434 };
436 };
435
437
436 this.initStatusChangeSelector = function(){
438 this.initStatusChangeSelector = function(){
437 var formatChangeStatus = function(state, escapeMarkup) {
439 var formatChangeStatus = function(state, escapeMarkup) {
438 var originalOption = state.element;
440 var originalOption = state.element;
439 return '<div class="flag_status ' + $(originalOption).data('status') + ' pull-left"></div>' +
441 return '<div class="flag_status ' + $(originalOption).data('status') + ' pull-left"></div>' +
440 '<span>' + escapeMarkup(state.text) + '</span>';
442 '<span>' + escapeMarkup(state.text) + '</span>';
441 };
443 };
442 var formatResult = function(result, container, query, escapeMarkup) {
444 var formatResult = function(result, container, query, escapeMarkup) {
443 return formatChangeStatus(result, escapeMarkup);
445 return formatChangeStatus(result, escapeMarkup);
444 };
446 };
445
447
446 var formatSelection = function(data, container, escapeMarkup) {
448 var formatSelection = function(data, container, escapeMarkup) {
447 return formatChangeStatus(data, escapeMarkup);
449 return formatChangeStatus(data, escapeMarkup);
448 };
450 };
449
451
450 $(this.submitForm).find(this.statusChange).select2({
452 $(this.submitForm).find(this.statusChange).select2({
451 placeholder: _gettext('Status Review'),
453 placeholder: _gettext('Status Review'),
452 formatResult: formatResult,
454 formatResult: formatResult,
453 formatSelection: formatSelection,
455 formatSelection: formatSelection,
454 containerCssClass: "drop-menu status_box_menu",
456 containerCssClass: "drop-menu status_box_menu",
455 dropdownCssClass: "drop-menu-dropdown",
457 dropdownCssClass: "drop-menu-dropdown",
456 dropdownAutoWidth: true,
458 dropdownAutoWidth: true,
457 minimumResultsForSearch: -1
459 minimumResultsForSearch: -1
458 });
460 });
459 $(this.submitForm).find(this.statusChange).on('change', function() {
461 $(this.submitForm).find(this.statusChange).on('change', function() {
460 var status = self.getCommentStatus();
462 var status = self.getCommentStatus();
461 if (status && !self.lineNo) {
463 if (status && !self.lineNo) {
462 $(self.submitButton).prop('disabled', false);
464 $(self.submitButton).prop('disabled', false);
463 }
465 }
464 //todo, fix this name
466 //todo, fix this name
465 var placeholderText = _gettext('Comment text will be set automatically based on currently selected status ({0}) ...').format(status);
467 var placeholderText = _gettext('Comment text will be set automatically based on currently selected status ({0}) ...').format(status);
466 self.cm.setOption('placeholder', placeholderText);
468 self.cm.setOption('placeholder', placeholderText);
467 })
469 })
468 };
470 };
469
471
470 // reset the comment form into it's original state
472 // reset the comment form into it's original state
471 this.resetCommentFormState = function(content) {
473 this.resetCommentFormState = function(content) {
472 content = content || '';
474 content = content || '';
473
475
474 $(this.editContainer).show();
476 $(this.editContainer).show();
475 $(this.editButton).hide();
477 $(this.editButton).hide();
476
478
477 $(this.previewContainer).hide();
479 $(this.previewContainer).hide();
478 $(this.previewButton).show();
480 $(this.previewButton).show();
479
481
480 this.setActionButtonsDisabled(true);
482 this.setActionButtonsDisabled(true);
481 self.cm.setValue(content);
483 self.cm.setValue(content);
482 self.cm.setOption("readOnly", false);
484 self.cm.setOption("readOnly", false);
483 };
485 };
484
486
485 this.submitAjaxPOST = function(url, postData, successHandler, failHandler) {
487 this.submitAjaxPOST = function(url, postData, successHandler, failHandler) {
486 failHandler = failHandler || function() {};
488 failHandler = failHandler || function() {};
487 var postData = toQueryString(postData);
489 var postData = toQueryString(postData);
488 var request = $.ajax({
490 var request = $.ajax({
489 url: url,
491 url: url,
490 type: 'POST',
492 type: 'POST',
491 data: postData,
493 data: postData,
492 headers: {'X-PARTIAL-XHR': true}
494 headers: {'X-PARTIAL-XHR': true}
493 })
495 })
494 .done(function(data) {
496 .done(function(data) {
495 successHandler(data);
497 successHandler(data);
496 })
498 })
497 .fail(function(data, textStatus, errorThrown){
499 .fail(function(data, textStatus, errorThrown){
498 alert(
500 alert(
499 "Error while submitting comment.\n" +
501 "Error while submitting comment.\n" +
500 "Error code {0} ({1}).".format(data.status, data.statusText));
502 "Error code {0} ({1}).".format(data.status, data.statusText));
501 failHandler()
503 failHandler()
502 });
504 });
503 return request;
505 return request;
504 };
506 };
505
507
506 // overwrite a submitHandler, we need to do it for inline comments
508 // overwrite a submitHandler, we need to do it for inline comments
507 this.setHandleFormSubmit = function(callback) {
509 this.setHandleFormSubmit = function(callback) {
508 this.handleFormSubmit = callback;
510 this.handleFormSubmit = callback;
509 };
511 };
510
512
511 // default handler for for submit for main comments
513 // default handler for for submit for main comments
512 this.handleFormSubmit = function() {
514 this.handleFormSubmit = function() {
513 var text = self.cm.getValue();
515 var text = self.cm.getValue();
514 var status = self.getCommentStatus();
516 var status = self.getCommentStatus();
515
517
516 if (text === "" && !status) {
518 if (text === "" && !status) {
517 return;
519 return;
518 }
520 }
519
521
520 var excludeCancelBtn = false;
522 var excludeCancelBtn = false;
521 var submitEvent = true;
523 var submitEvent = true;
522 self.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
524 self.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
523 self.cm.setOption("readOnly", true);
525 self.cm.setOption("readOnly", true);
524 var postData = {
526 var postData = {
525 'text': text,
527 'text': text,
526 'changeset_status': status,
528 'changeset_status': status,
527 'csrf_token': CSRF_TOKEN
529 'csrf_token': CSRF_TOKEN
528 };
530 };
529
531
530 var submitSuccessCallback = function(o) {
532 var submitSuccessCallback = function(o) {
531 if (status) {
533 if (status) {
532 location.reload(true);
534 location.reload(true);
533 } else {
535 } else {
534 $('#injected_page_comments').append(o.rendered_text);
536 $('#injected_page_comments').append(o.rendered_text);
535 self.resetCommentFormState();
537 self.resetCommentFormState();
536 bindDeleteCommentButtons();
538 bindDeleteCommentButtons();
537 timeagoActivate();
539 timeagoActivate();
538 }
540 }
539 };
541 };
540 var submitFailCallback = function(){
542 var submitFailCallback = function(){
541 self.resetCommentFormState(text)
543 self.resetCommentFormState(text)
542 };
544 };
543 self.submitAjaxPOST(
545 self.submitAjaxPOST(
544 self.submitUrl, postData, submitSuccessCallback, submitFailCallback);
546 self.submitUrl, postData, submitSuccessCallback, submitFailCallback);
545 };
547 };
546
548
547 this.previewSuccessCallback = function(o) {
549 this.previewSuccessCallback = function(o) {
548 $(self.previewBoxSelector).html(o);
550 $(self.previewBoxSelector).html(o);
549 $(self.previewBoxSelector).removeClass('unloaded');
551 $(self.previewBoxSelector).removeClass('unloaded');
550
552
551 // swap buttons
553 // swap buttons
552 $(self.previewButton).hide();
554 $(self.previewButton).hide();
553 $(self.editButton).show();
555 $(self.editButton).show();
554
556
555 // unlock buttons
557 // unlock buttons
556 self.setActionButtonsDisabled(false);
558 self.setActionButtonsDisabled(false);
557 };
559 };
558
560
559 this.setActionButtonsDisabled = function(state, excludeCancelBtn, submitEvent) {
561 this.setActionButtonsDisabled = function(state, excludeCancelBtn, submitEvent) {
560 excludeCancelBtn = excludeCancelBtn || false;
562 excludeCancelBtn = excludeCancelBtn || false;
561 submitEvent = submitEvent || false;
563 submitEvent = submitEvent || false;
562
564
563 $(this.editButton).prop('disabled', state);
565 $(this.editButton).prop('disabled', state);
564 $(this.previewButton).prop('disabled', state);
566 $(this.previewButton).prop('disabled', state);
565
567
566 if (!excludeCancelBtn) {
568 if (!excludeCancelBtn) {
567 $(this.cancelButton).prop('disabled', state);
569 $(this.cancelButton).prop('disabled', state);
568 }
570 }
569
571
570 var submitState = state;
572 var submitState = state;
571 if (!submitEvent && this.getCommentStatus() && !this.lineNo) {
573 if (!submitEvent && this.getCommentStatus() && !this.lineNo) {
572 // if the value of commit review status is set, we allow
574 // if the value of commit review status is set, we allow
573 // submit button, but only on Main form, lineNo means inline
575 // submit button, but only on Main form, lineNo means inline
574 submitState = false
576 submitState = false
575 }
577 }
576 $(this.submitButton).prop('disabled', submitState);
578 $(this.submitButton).prop('disabled', submitState);
577 if (submitEvent) {
579 if (submitEvent) {
578 $(this.submitButton).val(_gettext('Submitting...'));
580 $(this.submitButton).val(_gettext('Submitting...'));
579 } else {
581 } else {
580 $(this.submitButton).val(this.submitButtonText);
582 $(this.submitButton).val(this.submitButtonText);
581 }
583 }
582
584
583 };
585 };
584
586
585 // lock preview/edit/submit buttons on load, but exclude cancel button
587 // lock preview/edit/submit buttons on load, but exclude cancel button
586 var excludeCancelBtn = true;
588 var excludeCancelBtn = true;
587 this.setActionButtonsDisabled(true, excludeCancelBtn);
589 this.setActionButtonsDisabled(true, excludeCancelBtn);
588
590
589 // anonymous users don't have access to initialized CM instance
591 // anonymous users don't have access to initialized CM instance
590 if (this.cm !== undefined){
592 if (this.cm !== undefined){
591 this.cm.on('change', function(cMirror) {
593 this.cm.on('change', function(cMirror) {
592 if (cMirror.getValue() === "") {
594 if (cMirror.getValue() === "") {
593 self.setActionButtonsDisabled(true, excludeCancelBtn)
595 self.setActionButtonsDisabled(true, excludeCancelBtn)
594 } else {
596 } else {
595 self.setActionButtonsDisabled(false, excludeCancelBtn)
597 self.setActionButtonsDisabled(false, excludeCancelBtn)
596 }
598 }
597 });
599 });
598 }
600 }
599
601
600 $(this.editButton).on('click', function(e) {
602 $(this.editButton).on('click', function(e) {
601 e.preventDefault();
603 e.preventDefault();
602
604
603 $(self.previewButton).show();
605 $(self.previewButton).show();
604 $(self.previewContainer).hide();
606 $(self.previewContainer).hide();
605 $(self.editButton).hide();
607 $(self.editButton).hide();
606 $(self.editContainer).show();
608 $(self.editContainer).show();
607
609
608 });
610 });
609
611
610 $(this.previewButton).on('click', function(e) {
612 $(this.previewButton).on('click', function(e) {
611 e.preventDefault();
613 e.preventDefault();
612 var text = self.cm.getValue();
614 var text = self.cm.getValue();
613
615
614 if (text === "") {
616 if (text === "") {
615 return;
617 return;
616 }
618 }
617
619
618 var postData = {
620 var postData = {
619 'text': text,
621 'text': text,
620 'renderer': DEFAULT_RENDERER,
622 'renderer': DEFAULT_RENDERER,
621 'csrf_token': CSRF_TOKEN
623 'csrf_token': CSRF_TOKEN
622 };
624 };
623
625
624 // lock ALL buttons on preview
626 // lock ALL buttons on preview
625 self.setActionButtonsDisabled(true);
627 self.setActionButtonsDisabled(true);
626
628
627 $(self.previewBoxSelector).addClass('unloaded');
629 $(self.previewBoxSelector).addClass('unloaded');
628 $(self.previewBoxSelector).html(_gettext('Loading ...'));
630 $(self.previewBoxSelector).html(_gettext('Loading ...'));
629 $(self.editContainer).hide();
631 $(self.editContainer).hide();
630 $(self.previewContainer).show();
632 $(self.previewContainer).show();
631
633
632 // by default we reset state of comment preserving the text
634 // by default we reset state of comment preserving the text
633 var previewFailCallback = function(){
635 var previewFailCallback = function(){
634 self.resetCommentFormState(text)
636 self.resetCommentFormState(text)
635 };
637 };
636 self.submitAjaxPOST(
638 self.submitAjaxPOST(
637 self.previewUrl, postData, self.previewSuccessCallback, previewFailCallback);
639 self.previewUrl, postData, self.previewSuccessCallback, previewFailCallback);
638
640
639 });
641 });
640
642
641 $(this.submitForm).submit(function(e) {
643 $(this.submitForm).submit(function(e) {
642 e.preventDefault();
644 e.preventDefault();
643 var allowedToSubmit = self.isAllowedToSubmit();
645 var allowedToSubmit = self.isAllowedToSubmit();
644 if (!allowedToSubmit){
646 if (!allowedToSubmit){
645 return false;
647 return false;
646 }
648 }
647 self.handleFormSubmit();
649 self.handleFormSubmit();
648 });
650 });
649
651
650 }
652 }
651
653
652 return CommentForm;
654 return CommentForm;
653 })();
655 })();
General Comments 0
You need to be logged in to leave comments. Login now