##// END OF EJS Templates
js: add check to eliminate js errors when inline comment function references an object which does not exist
lisaq -
r695:bbba5e47 default
parent child Browse files
Show More
@@ -1,655 +1,658 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,
216 lineno: lineno,
217 f_path: f_path}
217 f_path: f_path}
218 );
218 );
219 };
219 };
220
220
221 var deleteComment = function(comment_id) {
221 var deleteComment = function(comment_id) {
222 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id);
222 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id);
223 var postData = {
223 var postData = {
224 '_method': 'delete',
224 '_method': 'delete',
225 'csrf_token': CSRF_TOKEN
225 'csrf_token': CSRF_TOKEN
226 };
226 };
227
227
228 var success = function(o) {
228 var success = function(o) {
229 window.location.reload();
229 window.location.reload();
230 };
230 };
231 ajaxPOST(url, postData, success);
231 ajaxPOST(url, postData, success);
232 };
232 };
233
233
234 var createInlineAddButton = function(tr){
234 var createInlineAddButton = function(tr){
235 var label = _gettext('Add another comment');
235 var label = _gettext('Add another comment');
236 var html_el = document.createElement('div');
236 var html_el = document.createElement('div');
237 $(html_el).addClass('add-comment');
237 $(html_el).addClass('add-comment');
238 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);
239 var add = new $(html_el);
239 var add = new $(html_el);
240 add.on('click', function(e) {
240 add.on('click', function(e) {
241 injectInlineForm(tr);
241 injectInlineForm(tr);
242 });
242 });
243 return add;
243 return add;
244 };
244 };
245
245
246 var placeAddButton = function(target_tr){
246 var placeAddButton = function(target_tr){
247 if(!target_tr){
247 if(!target_tr){
248 return;
248 return;
249 }
249 }
250 var last_node = target_tr;
250 var last_node = target_tr;
251 // scan
251 // scan
252 while (1){
252 while (1){
253 var n = last_node.nextElementSibling;
253 var n = last_node.nextElementSibling;
254 // next element are comments !
254 // next element are comments !
255 if($(n).hasClass('inline-comments')){
255 if($(n).hasClass('inline-comments')){
256 last_node = n;
256 last_node = n;
257 // also remove the comment button from previous
257 // also remove the comment button from previous
258 var comment_add_buttons = $(last_node).find('.add-comment');
258 var comment_add_buttons = $(last_node).find('.add-comment');
259 for(var i=0; i<comment_add_buttons.length; i++){
259 for(var i=0; i<comment_add_buttons.length; i++){
260 var b = comment_add_buttons[i];
260 var b = comment_add_buttons[i];
261 b.parentNode.removeChild(b);
261 b.parentNode.removeChild(b);
262 }
262 }
263 }
263 }
264 else{
264 else{
265 break;
265 break;
266 }
266 }
267 }
267 }
268 var add = createInlineAddButton(target_tr);
268 var add = createInlineAddButton(target_tr);
269 // get the comment div
269 // get the comment div
270 var comment_block = $(last_node).find('.comment')[0];
270 var comment_block = $(last_node).find('.comment')[0];
271 // attach add button
271 // attach add button
272 $(add).insertAfter(comment_block);
272 $(add).insertAfter(comment_block);
273 };
273 };
274
274
275 /**
275 /**
276 * 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
277 */
277 */
278 var placeInline = function(target_container, lineno, html, show_add_button) {
278 var placeInline = function(target_container, lineno, html, show_add_button) {
279 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;
280
280
281 var lineid = "{0}_{1}".format(target_container, lineno);
281 var lineid = "{0}_{1}".format(target_container, lineno);
282 var target_line = $('#' + lineid).get(0);
282 var target_line = $('#' + lineid).get(0);
283 var comment = new $(tableTr('inline-comments', html));
283 var comment = new $(tableTr('inline-comments', html));
284 // check if there are comments already !
284 // check if there are comments already !
285 var parent_node = target_line.parentNode;
285 if (target_line) {
286 var root_parent = parent_node;
286 var parent_node = target_line.parentNode;
287 while (1) {
287 var root_parent = parent_node;
288 var n = parent_node.nextElementSibling;
288
289 // next element are comments !
289 while (1) {
290 if ($(n).hasClass('inline-comments')) {
290 var n = parent_node.nextElementSibling;
291 parent_node = n;
291 // next element are comments !
292 if ($(n).hasClass('inline-comments')) {
293 parent_node = n;
294 }
295 else {
296 break;
297 }
292 }
298 }
293 else {
299 // put in the comment at the bottom
294 break;
300 $(comment).insertAfter(parent_node);
301 $(comment).find('.comment-inline').addClass('inline-comment-injected');
302 // scan nodes, and attach add button to last one
303 if (show_add_button) {
304 placeAddButton(root_parent);
295 }
305 }
296 }
306 }
297 // put in the comment at the bottom
298 $(comment).insertAfter(parent_node);
299 $(comment).find('.comment-inline').addClass('inline-comment-injected');
300 // scan nodes, and attach add button to last one
301 if (show_add_button) {
302 placeAddButton(root_parent);
303 }
304
307
305 return target_line;
308 return target_line;
306 };
309 };
307
310
308 var linkifyComments = function(comments) {
311 var linkifyComments = function(comments) {
309
312
310 for (var i = 0; i < comments.length; i++) {
313 for (var i = 0; i < comments.length; i++) {
311 var comment_id = $(comments[i]).data('comment-id');
314 var comment_id = $(comments[i]).data('comment-id');
312 var prev_comment_id = $(comments[i - 1]).data('comment-id');
315 var prev_comment_id = $(comments[i - 1]).data('comment-id');
313 var next_comment_id = $(comments[i + 1]).data('comment-id');
316 var next_comment_id = $(comments[i + 1]).data('comment-id');
314
317
315 // place next/prev links
318 // place next/prev links
316 if (prev_comment_id) {
319 if (prev_comment_id) {
317 $('#prev_c_' + comment_id).show();
320 $('#prev_c_' + comment_id).show();
318 $('#prev_c_' + comment_id + " a.arrow_comment_link").attr(
321 $('#prev_c_' + comment_id + " a.arrow_comment_link").attr(
319 'href', '#comment-' + prev_comment_id).removeClass('disabled');
322 'href', '#comment-' + prev_comment_id).removeClass('disabled');
320 }
323 }
321 if (next_comment_id) {
324 if (next_comment_id) {
322 $('#next_c_' + comment_id).show();
325 $('#next_c_' + comment_id).show();
323 $('#next_c_' + comment_id + " a.arrow_comment_link").attr(
326 $('#next_c_' + comment_id + " a.arrow_comment_link").attr(
324 'href', '#comment-' + next_comment_id).removeClass('disabled');
327 'href', '#comment-' + next_comment_id).removeClass('disabled');
325 }
328 }
326 // place a first link to the total counter
329 // place a first link to the total counter
327 if (i === 0) {
330 if (i === 0) {
328 $('#inline-comments-counter').attr('href', '#comment-' + comment_id);
331 $('#inline-comments-counter').attr('href', '#comment-' + comment_id);
329 }
332 }
330 }
333 }
331
334
332 };
335 };
333
336
334 /**
337 /**
335 * Iterates over all the inlines, and places them inside proper blocks of data
338 * Iterates over all the inlines, and places them inside proper blocks of data
336 */
339 */
337 var renderInlineComments = function(file_comments, show_add_button) {
340 var renderInlineComments = function(file_comments, show_add_button) {
338 show_add_button = typeof show_add_button !== 'undefined' ? show_add_button : true;
341 show_add_button = typeof show_add_button !== 'undefined' ? show_add_button : true;
339
342
340 for (var i = 0; i < file_comments.length; i++) {
343 for (var i = 0; i < file_comments.length; i++) {
341 var box = file_comments[i];
344 var box = file_comments[i];
342
345
343 var target_id = $(box).attr('target_id');
346 var target_id = $(box).attr('target_id');
344
347
345 // actually comments with line numbers
348 // actually comments with line numbers
346 var comments = box.children;
349 var comments = box.children;
347
350
348 for (var j = 0; j < comments.length; j++) {
351 for (var j = 0; j < comments.length; j++) {
349 var data = {
352 var data = {
350 'rendered_text': comments[j].outerHTML,
353 'rendered_text': comments[j].outerHTML,
351 'line_no': $(comments[j]).attr('line'),
354 'line_no': $(comments[j]).attr('line'),
352 'target_id': target_id
355 'target_id': target_id
353 };
356 };
354 renderInlineComment(data, show_add_button);
357 renderInlineComment(data, show_add_button);
355 }
358 }
356 }
359 }
357
360
358 // since order of injection is random, we're now re-iterating
361 // since order of injection is random, we're now re-iterating
359 // from correct order and filling in links
362 // from correct order and filling in links
360 linkifyComments($('.inline-comment-injected'));
363 linkifyComments($('.inline-comment-injected'));
361 bindDeleteCommentButtons();
364 bindDeleteCommentButtons();
362 firefoxAnchorFix();
365 firefoxAnchorFix();
363 };
366 };
364
367
365
368
366 /* Comment form for main and inline comments */
369 /* Comment form for main and inline comments */
367 var CommentForm = (function() {
370 var CommentForm = (function() {
368 "use strict";
371 "use strict";
369
372
370 function CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions) {
373 function CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions) {
371
374
372 this.withLineNo = function(selector) {
375 this.withLineNo = function(selector) {
373 var lineNo = this.lineNo;
376 var lineNo = this.lineNo;
374 if (lineNo === undefined) {
377 if (lineNo === undefined) {
375 return selector
378 return selector
376 } else {
379 } else {
377 return selector + '_' + lineNo;
380 return selector + '_' + lineNo;
378 }
381 }
379 };
382 };
380
383
381 this.commitId = commitId;
384 this.commitId = commitId;
382 this.pullRequestId = pullRequestId;
385 this.pullRequestId = pullRequestId;
383 this.lineNo = lineNo;
386 this.lineNo = lineNo;
384 this.initAutocompleteActions = initAutocompleteActions;
387 this.initAutocompleteActions = initAutocompleteActions;
385
388
386 this.previewButton = this.withLineNo('#preview-btn');
389 this.previewButton = this.withLineNo('#preview-btn');
387 this.previewContainer = this.withLineNo('#preview-container');
390 this.previewContainer = this.withLineNo('#preview-container');
388
391
389 this.previewBoxSelector = this.withLineNo('#preview-box');
392 this.previewBoxSelector = this.withLineNo('#preview-box');
390
393
391 this.editButton = this.withLineNo('#edit-btn');
394 this.editButton = this.withLineNo('#edit-btn');
392 this.editContainer = this.withLineNo('#edit-container');
395 this.editContainer = this.withLineNo('#edit-container');
393
396
394 this.cancelButton = this.withLineNo('#cancel-btn');
397 this.cancelButton = this.withLineNo('#cancel-btn');
395
398
396 this.statusChange = '#change_status';
399 this.statusChange = '#change_status';
397 this.cmBox = this.withLineNo('#text');
400 this.cmBox = this.withLineNo('#text');
398 this.cm = initCommentBoxCodeMirror(this.cmBox, this.initAutocompleteActions);
401 this.cm = initCommentBoxCodeMirror(this.cmBox, this.initAutocompleteActions);
399
402
400 this.submitForm = formElement;
403 this.submitForm = formElement;
401 this.submitButton = $(this.submitForm).find('input[type="submit"]');
404 this.submitButton = $(this.submitForm).find('input[type="submit"]');
402 this.submitButtonText = this.submitButton.val();
405 this.submitButtonText = this.submitButton.val();
403
406
404 this.previewUrl = pyroutes.url('changeset_comment_preview',
407 this.previewUrl = pyroutes.url('changeset_comment_preview',
405 {'repo_name': templateContext.repo_name});
408 {'repo_name': templateContext.repo_name});
406
409
407 // based on commitId, or pullReuqestId decide where do we submit
410 // based on commitId, or pullReuqestId decide where do we submit
408 // out data
411 // out data
409 if (this.commitId){
412 if (this.commitId){
410 this.submitUrl = pyroutes.url('changeset_comment',
413 this.submitUrl = pyroutes.url('changeset_comment',
411 {'repo_name': templateContext.repo_name,
414 {'repo_name': templateContext.repo_name,
412 'revision': this.commitId});
415 'revision': this.commitId});
413
416
414 } else if (this.pullRequestId) {
417 } else if (this.pullRequestId) {
415 this.submitUrl = pyroutes.url('pullrequest_comment',
418 this.submitUrl = pyroutes.url('pullrequest_comment',
416 {'repo_name': templateContext.repo_name,
419 {'repo_name': templateContext.repo_name,
417 'pull_request_id': this.pullRequestId});
420 'pull_request_id': this.pullRequestId});
418
421
419 } else {
422 } else {
420 throw new Error(
423 throw new Error(
421 'CommentForm requires pullRequestId, or commitId to be specified.')
424 'CommentForm requires pullRequestId, or commitId to be specified.')
422 }
425 }
423
426
424 this.getCmInstance = function(){
427 this.getCmInstance = function(){
425 return this.cm
428 return this.cm
426 };
429 };
427
430
428 var self = this;
431 var self = this;
429
432
430 this.getCommentStatus = function() {
433 this.getCommentStatus = function() {
431 return $(this.submitForm).find(this.statusChange).val();
434 return $(this.submitForm).find(this.statusChange).val();
432 };
435 };
433
436
434 this.isAllowedToSubmit = function() {
437 this.isAllowedToSubmit = function() {
435 return !$(this.submitButton).prop('disabled');
438 return !$(this.submitButton).prop('disabled');
436 };
439 };
437
440
438 this.initStatusChangeSelector = function(){
441 this.initStatusChangeSelector = function(){
439 var formatChangeStatus = function(state, escapeMarkup) {
442 var formatChangeStatus = function(state, escapeMarkup) {
440 var originalOption = state.element;
443 var originalOption = state.element;
441 return '<div class="flag_status ' + $(originalOption).data('status') + ' pull-left"></div>' +
444 return '<div class="flag_status ' + $(originalOption).data('status') + ' pull-left"></div>' +
442 '<span>' + escapeMarkup(state.text) + '</span>';
445 '<span>' + escapeMarkup(state.text) + '</span>';
443 };
446 };
444 var formatResult = function(result, container, query, escapeMarkup) {
447 var formatResult = function(result, container, query, escapeMarkup) {
445 return formatChangeStatus(result, escapeMarkup);
448 return formatChangeStatus(result, escapeMarkup);
446 };
449 };
447
450
448 var formatSelection = function(data, container, escapeMarkup) {
451 var formatSelection = function(data, container, escapeMarkup) {
449 return formatChangeStatus(data, escapeMarkup);
452 return formatChangeStatus(data, escapeMarkup);
450 };
453 };
451
454
452 $(this.submitForm).find(this.statusChange).select2({
455 $(this.submitForm).find(this.statusChange).select2({
453 placeholder: _gettext('Status Review'),
456 placeholder: _gettext('Status Review'),
454 formatResult: formatResult,
457 formatResult: formatResult,
455 formatSelection: formatSelection,
458 formatSelection: formatSelection,
456 containerCssClass: "drop-menu status_box_menu",
459 containerCssClass: "drop-menu status_box_menu",
457 dropdownCssClass: "drop-menu-dropdown",
460 dropdownCssClass: "drop-menu-dropdown",
458 dropdownAutoWidth: true,
461 dropdownAutoWidth: true,
459 minimumResultsForSearch: -1
462 minimumResultsForSearch: -1
460 });
463 });
461 $(this.submitForm).find(this.statusChange).on('change', function() {
464 $(this.submitForm).find(this.statusChange).on('change', function() {
462 var status = self.getCommentStatus();
465 var status = self.getCommentStatus();
463 if (status && !self.lineNo) {
466 if (status && !self.lineNo) {
464 $(self.submitButton).prop('disabled', false);
467 $(self.submitButton).prop('disabled', false);
465 }
468 }
466 //todo, fix this name
469 //todo, fix this name
467 var placeholderText = _gettext('Comment text will be set automatically based on currently selected status ({0}) ...').format(status);
470 var placeholderText = _gettext('Comment text will be set automatically based on currently selected status ({0}) ...').format(status);
468 self.cm.setOption('placeholder', placeholderText);
471 self.cm.setOption('placeholder', placeholderText);
469 })
472 })
470 };
473 };
471
474
472 // reset the comment form into it's original state
475 // reset the comment form into it's original state
473 this.resetCommentFormState = function(content) {
476 this.resetCommentFormState = function(content) {
474 content = content || '';
477 content = content || '';
475
478
476 $(this.editContainer).show();
479 $(this.editContainer).show();
477 $(this.editButton).hide();
480 $(this.editButton).hide();
478
481
479 $(this.previewContainer).hide();
482 $(this.previewContainer).hide();
480 $(this.previewButton).show();
483 $(this.previewButton).show();
481
484
482 this.setActionButtonsDisabled(true);
485 this.setActionButtonsDisabled(true);
483 self.cm.setValue(content);
486 self.cm.setValue(content);
484 self.cm.setOption("readOnly", false);
487 self.cm.setOption("readOnly", false);
485 };
488 };
486
489
487 this.submitAjaxPOST = function(url, postData, successHandler, failHandler) {
490 this.submitAjaxPOST = function(url, postData, successHandler, failHandler) {
488 failHandler = failHandler || function() {};
491 failHandler = failHandler || function() {};
489 var postData = toQueryString(postData);
492 var postData = toQueryString(postData);
490 var request = $.ajax({
493 var request = $.ajax({
491 url: url,
494 url: url,
492 type: 'POST',
495 type: 'POST',
493 data: postData,
496 data: postData,
494 headers: {'X-PARTIAL-XHR': true}
497 headers: {'X-PARTIAL-XHR': true}
495 })
498 })
496 .done(function(data) {
499 .done(function(data) {
497 successHandler(data);
500 successHandler(data);
498 })
501 })
499 .fail(function(data, textStatus, errorThrown){
502 .fail(function(data, textStatus, errorThrown){
500 alert(
503 alert(
501 "Error while submitting comment.\n" +
504 "Error while submitting comment.\n" +
502 "Error code {0} ({1}).".format(data.status, data.statusText));
505 "Error code {0} ({1}).".format(data.status, data.statusText));
503 failHandler()
506 failHandler()
504 });
507 });
505 return request;
508 return request;
506 };
509 };
507
510
508 // overwrite a submitHandler, we need to do it for inline comments
511 // overwrite a submitHandler, we need to do it for inline comments
509 this.setHandleFormSubmit = function(callback) {
512 this.setHandleFormSubmit = function(callback) {
510 this.handleFormSubmit = callback;
513 this.handleFormSubmit = callback;
511 };
514 };
512
515
513 // default handler for for submit for main comments
516 // default handler for for submit for main comments
514 this.handleFormSubmit = function() {
517 this.handleFormSubmit = function() {
515 var text = self.cm.getValue();
518 var text = self.cm.getValue();
516 var status = self.getCommentStatus();
519 var status = self.getCommentStatus();
517
520
518 if (text === "" && !status) {
521 if (text === "" && !status) {
519 return;
522 return;
520 }
523 }
521
524
522 var excludeCancelBtn = false;
525 var excludeCancelBtn = false;
523 var submitEvent = true;
526 var submitEvent = true;
524 self.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
527 self.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
525 self.cm.setOption("readOnly", true);
528 self.cm.setOption("readOnly", true);
526 var postData = {
529 var postData = {
527 'text': text,
530 'text': text,
528 'changeset_status': status,
531 'changeset_status': status,
529 'csrf_token': CSRF_TOKEN
532 'csrf_token': CSRF_TOKEN
530 };
533 };
531
534
532 var submitSuccessCallback = function(o) {
535 var submitSuccessCallback = function(o) {
533 if (status) {
536 if (status) {
534 location.reload(true);
537 location.reload(true);
535 } else {
538 } else {
536 $('#injected_page_comments').append(o.rendered_text);
539 $('#injected_page_comments').append(o.rendered_text);
537 self.resetCommentFormState();
540 self.resetCommentFormState();
538 bindDeleteCommentButtons();
541 bindDeleteCommentButtons();
539 timeagoActivate();
542 timeagoActivate();
540 }
543 }
541 };
544 };
542 var submitFailCallback = function(){
545 var submitFailCallback = function(){
543 self.resetCommentFormState(text)
546 self.resetCommentFormState(text)
544 };
547 };
545 self.submitAjaxPOST(
548 self.submitAjaxPOST(
546 self.submitUrl, postData, submitSuccessCallback, submitFailCallback);
549 self.submitUrl, postData, submitSuccessCallback, submitFailCallback);
547 };
550 };
548
551
549 this.previewSuccessCallback = function(o) {
552 this.previewSuccessCallback = function(o) {
550 $(self.previewBoxSelector).html(o);
553 $(self.previewBoxSelector).html(o);
551 $(self.previewBoxSelector).removeClass('unloaded');
554 $(self.previewBoxSelector).removeClass('unloaded');
552
555
553 // swap buttons
556 // swap buttons
554 $(self.previewButton).hide();
557 $(self.previewButton).hide();
555 $(self.editButton).show();
558 $(self.editButton).show();
556
559
557 // unlock buttons
560 // unlock buttons
558 self.setActionButtonsDisabled(false);
561 self.setActionButtonsDisabled(false);
559 };
562 };
560
563
561 this.setActionButtonsDisabled = function(state, excludeCancelBtn, submitEvent) {
564 this.setActionButtonsDisabled = function(state, excludeCancelBtn, submitEvent) {
562 excludeCancelBtn = excludeCancelBtn || false;
565 excludeCancelBtn = excludeCancelBtn || false;
563 submitEvent = submitEvent || false;
566 submitEvent = submitEvent || false;
564
567
565 $(this.editButton).prop('disabled', state);
568 $(this.editButton).prop('disabled', state);
566 $(this.previewButton).prop('disabled', state);
569 $(this.previewButton).prop('disabled', state);
567
570
568 if (!excludeCancelBtn) {
571 if (!excludeCancelBtn) {
569 $(this.cancelButton).prop('disabled', state);
572 $(this.cancelButton).prop('disabled', state);
570 }
573 }
571
574
572 var submitState = state;
575 var submitState = state;
573 if (!submitEvent && this.getCommentStatus() && !this.lineNo) {
576 if (!submitEvent && this.getCommentStatus() && !this.lineNo) {
574 // if the value of commit review status is set, we allow
577 // if the value of commit review status is set, we allow
575 // submit button, but only on Main form, lineNo means inline
578 // submit button, but only on Main form, lineNo means inline
576 submitState = false
579 submitState = false
577 }
580 }
578 $(this.submitButton).prop('disabled', submitState);
581 $(this.submitButton).prop('disabled', submitState);
579 if (submitEvent) {
582 if (submitEvent) {
580 $(this.submitButton).val(_gettext('Submitting...'));
583 $(this.submitButton).val(_gettext('Submitting...'));
581 } else {
584 } else {
582 $(this.submitButton).val(this.submitButtonText);
585 $(this.submitButton).val(this.submitButtonText);
583 }
586 }
584
587
585 };
588 };
586
589
587 // lock preview/edit/submit buttons on load, but exclude cancel button
590 // lock preview/edit/submit buttons on load, but exclude cancel button
588 var excludeCancelBtn = true;
591 var excludeCancelBtn = true;
589 this.setActionButtonsDisabled(true, excludeCancelBtn);
592 this.setActionButtonsDisabled(true, excludeCancelBtn);
590
593
591 // anonymous users don't have access to initialized CM instance
594 // anonymous users don't have access to initialized CM instance
592 if (this.cm !== undefined){
595 if (this.cm !== undefined){
593 this.cm.on('change', function(cMirror) {
596 this.cm.on('change', function(cMirror) {
594 if (cMirror.getValue() === "") {
597 if (cMirror.getValue() === "") {
595 self.setActionButtonsDisabled(true, excludeCancelBtn)
598 self.setActionButtonsDisabled(true, excludeCancelBtn)
596 } else {
599 } else {
597 self.setActionButtonsDisabled(false, excludeCancelBtn)
600 self.setActionButtonsDisabled(false, excludeCancelBtn)
598 }
601 }
599 });
602 });
600 }
603 }
601
604
602 $(this.editButton).on('click', function(e) {
605 $(this.editButton).on('click', function(e) {
603 e.preventDefault();
606 e.preventDefault();
604
607
605 $(self.previewButton).show();
608 $(self.previewButton).show();
606 $(self.previewContainer).hide();
609 $(self.previewContainer).hide();
607 $(self.editButton).hide();
610 $(self.editButton).hide();
608 $(self.editContainer).show();
611 $(self.editContainer).show();
609
612
610 });
613 });
611
614
612 $(this.previewButton).on('click', function(e) {
615 $(this.previewButton).on('click', function(e) {
613 e.preventDefault();
616 e.preventDefault();
614 var text = self.cm.getValue();
617 var text = self.cm.getValue();
615
618
616 if (text === "") {
619 if (text === "") {
617 return;
620 return;
618 }
621 }
619
622
620 var postData = {
623 var postData = {
621 'text': text,
624 'text': text,
622 'renderer': DEFAULT_RENDERER,
625 'renderer': DEFAULT_RENDERER,
623 'csrf_token': CSRF_TOKEN
626 'csrf_token': CSRF_TOKEN
624 };
627 };
625
628
626 // lock ALL buttons on preview
629 // lock ALL buttons on preview
627 self.setActionButtonsDisabled(true);
630 self.setActionButtonsDisabled(true);
628
631
629 $(self.previewBoxSelector).addClass('unloaded');
632 $(self.previewBoxSelector).addClass('unloaded');
630 $(self.previewBoxSelector).html(_gettext('Loading ...'));
633 $(self.previewBoxSelector).html(_gettext('Loading ...'));
631 $(self.editContainer).hide();
634 $(self.editContainer).hide();
632 $(self.previewContainer).show();
635 $(self.previewContainer).show();
633
636
634 // by default we reset state of comment preserving the text
637 // by default we reset state of comment preserving the text
635 var previewFailCallback = function(){
638 var previewFailCallback = function(){
636 self.resetCommentFormState(text)
639 self.resetCommentFormState(text)
637 };
640 };
638 self.submitAjaxPOST(
641 self.submitAjaxPOST(
639 self.previewUrl, postData, self.previewSuccessCallback, previewFailCallback);
642 self.previewUrl, postData, self.previewSuccessCallback, previewFailCallback);
640
643
641 });
644 });
642
645
643 $(this.submitForm).submit(function(e) {
646 $(this.submitForm).submit(function(e) {
644 e.preventDefault();
647 e.preventDefault();
645 var allowedToSubmit = self.isAllowedToSubmit();
648 var allowedToSubmit = self.isAllowedToSubmit();
646 if (!allowedToSubmit){
649 if (!allowedToSubmit){
647 return false;
650 return false;
648 }
651 }
649 self.handleFormSubmit();
652 self.handleFormSubmit();
650 });
653 });
651
654
652 }
655 }
653
656
654 return CommentForm;
657 return CommentForm;
655 })();
658 })();
General Comments 0
You need to be logged in to leave comments. Login now