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