comments.js
831 lines
| 28.3 KiB
| application/javascript
|
JavascriptLexer
r1271 | // # Copyright (C) 2010-2017 RhodeCode GmbH | |||
r1 | // # | |||
// # This program is free software: you can redistribute it and/or modify | ||||
// # it under the terms of the GNU Affero General Public License, version 3 | ||||
// # (only), as published by the Free Software Foundation. | ||||
// # | ||||
// # This program is distributed in the hope that it will be useful, | ||||
// # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
// # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
// # GNU General Public License for more details. | ||||
// # | ||||
// # You should have received a copy of the GNU Affero General Public License | ||||
// # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
// # | ||||
// # This program is dual-licensed. If you wish to learn more about the | ||||
// # RhodeCode Enterprise Edition, including its added features, Support services, | ||||
// # and proprietary license terms, please see https://rhodecode.com/licenses/ | ||||
var firefoxAnchorFix = function() { | ||||
// hack to make anchor links behave properly on firefox, in our inline | ||||
// comments generation when comments are injected firefox is misbehaving | ||||
// when jumping to anchor links | ||||
if (location.href.indexOf('#') > -1) { | ||||
location.href += ''; | ||||
} | ||||
}; | ||||
var linkifyComments = function(comments) { | ||||
r1329 | var firstCommentId = null; | |||
if (comments) { | ||||
firstCommentId = $(comments[0]).data('comment-id'); | ||||
r1 | } | |||
r1329 | if (firstCommentId){ | |||
$('#inline-comments-counter').attr('href', '#comment-' + firstCommentId); | ||||
} | ||||
r1 | }; | |||
r1143 | ||||
r1326 | var bindToggleButtons = function() { | |||
$('.comment-toggle').on('click', function() { | ||||
$(this).parent().nextUntil('tr.line').toggle('inline-comments'); | ||||
}); | ||||
}; | ||||
r1 | ||||
/* Comment form for main and inline comments */ | ||||
r1331 | (function(mod) { | |||
r1325 | ||||
r1331 | if (typeof exports == "object" && typeof module == "object") { | |||
// CommonJS | ||||
module.exports = mod(); | ||||
} | ||||
else { | ||||
// Plain browser env | ||||
(this || window).CommentForm = mod(); | ||||
} | ||||
r1325 | ||||
})(function() { | ||||
r1 | "use strict"; | |||
r1325 | function CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId) { | |||
if (!(this instanceof CommentForm)) { | ||||
return new CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId); | ||||
} | ||||
// bind the element instance to our Form | ||||
$(formElement).get(0).CommentForm = this; | ||||
r1 | ||||
this.withLineNo = function(selector) { | ||||
var lineNo = this.lineNo; | ||||
if (lineNo === undefined) { | ||||
return selector | ||||
} else { | ||||
return selector + '_' + lineNo; | ||||
} | ||||
}; | ||||
this.commitId = commitId; | ||||
this.pullRequestId = pullRequestId; | ||||
this.lineNo = lineNo; | ||||
this.initAutocompleteActions = initAutocompleteActions; | ||||
this.previewButton = this.withLineNo('#preview-btn'); | ||||
this.previewContainer = this.withLineNo('#preview-container'); | ||||
this.previewBoxSelector = this.withLineNo('#preview-box'); | ||||
this.editButton = this.withLineNo('#edit-btn'); | ||||
this.editContainer = this.withLineNo('#edit-container'); | ||||
r1324 | this.cancelButton = this.withLineNo('#cancel-btn'); | |||
this.commentType = this.withLineNo('#comment_type'); | ||||
r1 | ||||
r1325 | this.resolvesId = null; | |||
this.resolvesActionId = null; | ||||
r1445 | this.closesPr = '#close_pull_request'; | |||
r1324 | this.cmBox = this.withLineNo('#text'); | |||
r1362 | this.cm = initCommentBoxCodeMirror(this, this.cmBox, this.initAutocompleteActions); | |||
r1 | ||||
r1326 | this.statusChange = this.withLineNo('#change_status'); | |||
r1 | ||||
this.submitForm = formElement; | ||||
this.submitButton = $(this.submitForm).find('input[type="submit"]'); | ||||
this.submitButtonText = this.submitButton.val(); | ||||
r1951 | this.previewUrl = pyroutes.url('repo_commit_comment_preview', | |||
{'repo_name': templateContext.repo_name, | ||||
'commit_id': templateContext.commit_data.commit_id}); | ||||
r1 | ||||
r1325 | if (resolvesCommentId){ | |||
this.resolvesId = '#resolve_comment_{0}'.format(resolvesCommentId); | ||||
this.resolvesActionId = '#resolve_comment_action_{0}'.format(resolvesCommentId); | ||||
$(this.commentType).prop('disabled', true); | ||||
$(this.commentType).addClass('disabled'); | ||||
r1326 | // disable select | |||
setTimeout(function() { | ||||
$(self.statusChange).select2('readonly', true); | ||||
}, 10); | ||||
r1325 | var resolvedInfo = ( | |||
r1344 | '<li class="resolve-action">' + | |||
r1325 | '<input type="hidden" id="resolve_comment_{0}" name="resolve_comment_{0}" value="{0}">' + | |||
'<button id="resolve_comment_action_{0}" class="resolve-text btn btn-sm" onclick="return Rhodecode.comments.submitResolution({0})">{1} #{0}</button>' + | ||||
'</li>' | ||||
).format(resolvesCommentId, _gettext('resolve comment')); | ||||
$(resolvedInfo).insertAfter($(this.commentType).parent()); | ||||
} | ||||
// based on commitId, or pullRequestId decide where do we submit | ||||
r1 | // out data | |||
if (this.commitId){ | ||||
r1951 | this.submitUrl = pyroutes.url('repo_commit_comment_create', | |||
r1 | {'repo_name': templateContext.repo_name, | |||
r1951 | 'commit_id': this.commitId}); | |||
this.selfUrl = pyroutes.url('repo_commit', | ||||
r1325 | {'repo_name': templateContext.repo_name, | |||
r1951 | 'commit_id': this.commitId}); | |||
r1 | ||||
} else if (this.pullRequestId) { | ||||
r1974 | this.submitUrl = pyroutes.url('pullrequest_comment_create', | |||
r1 | {'repo_name': templateContext.repo_name, | |||
'pull_request_id': this.pullRequestId}); | ||||
r1325 | this.selfUrl = pyroutes.url('pullrequest_show', | |||
{'repo_name': templateContext.repo_name, | ||||
'pull_request_id': this.pullRequestId}); | ||||
r1 | ||||
} else { | ||||
throw new Error( | ||||
'CommentForm requires pullRequestId, or commitId to be specified.') | ||||
} | ||||
r1326 | // FUNCTIONS and helpers | |||
var self = this; | ||||
this.isInline = function(){ | ||||
return this.lineNo && this.lineNo != 'general'; | ||||
}; | ||||
r1 | this.getCmInstance = function(){ | |||
return this.cm | ||||
}; | ||||
r1325 | this.setPlaceholder = function(placeholder) { | |||
var cm = this.getCmInstance(); | ||||
if (cm){ | ||||
cm.setOption('placeholder', placeholder); | ||||
} | ||||
}; | ||||
r1 | this.getCommentStatus = function() { | |||
return $(this.submitForm).find(this.statusChange).val(); | ||||
}; | ||||
r1324 | this.getCommentType = function() { | |||
return $(this.submitForm).find(this.commentType).val(); | ||||
}; | ||||
r1325 | ||||
this.getResolvesId = function() { | ||||
return $(this.submitForm).find(this.resolvesId).val() || null; | ||||
}; | ||||
r1445 | ||||
this.getClosePr = function() { | ||||
return $(this.submitForm).find(this.closesPr).val() || null; | ||||
}; | ||||
r1325 | this.markCommentResolved = function(resolvedCommentId){ | |||
$('#comment-label-{0}'.format(resolvedCommentId)).find('.resolved').show(); | ||||
$('#comment-label-{0}'.format(resolvedCommentId)).find('.resolve').hide(); | ||||
}; | ||||
r1 | this.isAllowedToSubmit = function() { | |||
return !$(this.submitButton).prop('disabled'); | ||||
}; | ||||
this.initStatusChangeSelector = function(){ | ||||
var formatChangeStatus = function(state, escapeMarkup) { | ||||
var originalOption = state.element; | ||||
return '<div class="flag_status ' + $(originalOption).data('status') + ' pull-left"></div>' + | ||||
'<span>' + escapeMarkup(state.text) + '</span>'; | ||||
}; | ||||
var formatResult = function(result, container, query, escapeMarkup) { | ||||
return formatChangeStatus(result, escapeMarkup); | ||||
}; | ||||
var formatSelection = function(data, container, escapeMarkup) { | ||||
return formatChangeStatus(data, escapeMarkup); | ||||
}; | ||||
$(this.submitForm).find(this.statusChange).select2({ | ||||
r325 | placeholder: _gettext('Status Review'), | |||
r1 | formatResult: formatResult, | |||
formatSelection: formatSelection, | ||||
containerCssClass: "drop-menu status_box_menu", | ||||
dropdownCssClass: "drop-menu-dropdown", | ||||
dropdownAutoWidth: true, | ||||
minimumResultsForSearch: -1 | ||||
}); | ||||
$(this.submitForm).find(this.statusChange).on('change', function() { | ||||
var status = self.getCommentStatus(); | ||||
r1445 | ||||
r1326 | if (status && !self.isInline()) { | |||
r1 | $(self.submitButton).prop('disabled', false); | |||
} | ||||
r1325 | ||||
r325 | var placeholderText = _gettext('Comment text will be set automatically based on currently selected status ({0}) ...').format(status); | |||
r1325 | self.setPlaceholder(placeholderText) | |||
r1 | }) | |||
}; | ||||
// reset the comment form into it's original state | ||||
this.resetCommentFormState = function(content) { | ||||
content = content || ''; | ||||
$(this.editContainer).show(); | ||||
r1281 | $(this.editButton).parent().addClass('active'); | |||
r1 | ||||
$(this.previewContainer).hide(); | ||||
r1281 | $(this.previewButton).parent().removeClass('active'); | |||
r1 | ||||
this.setActionButtonsDisabled(true); | ||||
self.cm.setValue(content); | ||||
self.cm.setOption("readOnly", false); | ||||
r1327 | ||||
if (this.resolvesId) { | ||||
// destroy the resolve action | ||||
$(this.resolvesId).parent().remove(); | ||||
} | ||||
r1445 | // reset closingPR flag | |||
$('.close-pr-input').remove(); | ||||
r1327 | ||||
$(this.statusChange).select2('readonly', false); | ||||
r1 | }; | |||
r1334 | this.globalSubmitSuccessCallback = function(){ | |||
// default behaviour is to call GLOBAL hook, if it's registered. | ||||
if (window.commentFormGlobalSubmitSuccessCallback !== undefined){ | ||||
commentFormGlobalSubmitSuccessCallback() | ||||
} | ||||
}; | ||||
r1331 | ||||
r1 | this.submitAjaxPOST = function(url, postData, successHandler, failHandler) { | |||
failHandler = failHandler || function() {}; | ||||
var postData = toQueryString(postData); | ||||
var request = $.ajax({ | ||||
url: url, | ||||
type: 'POST', | ||||
data: postData, | ||||
headers: {'X-PARTIAL-XHR': true} | ||||
}) | ||||
.done(function(data) { | ||||
successHandler(data); | ||||
}) | ||||
.fail(function(data, textStatus, errorThrown){ | ||||
alert( | ||||
"Error while submitting comment.\n" + | ||||
"Error code {0} ({1}).".format(data.status, data.statusText)); | ||||
failHandler() | ||||
}); | ||||
return request; | ||||
}; | ||||
// overwrite a submitHandler, we need to do it for inline comments | ||||
this.setHandleFormSubmit = function(callback) { | ||||
this.handleFormSubmit = callback; | ||||
}; | ||||
r1331 | // overwrite a submitSuccessHandler | |||
this.setGlobalSubmitSuccessCallback = function(callback) { | ||||
this.globalSubmitSuccessCallback = callback; | ||||
}; | ||||
r1 | // default handler for for submit for main comments | |||
this.handleFormSubmit = function() { | ||||
var text = self.cm.getValue(); | ||||
var status = self.getCommentStatus(); | ||||
r1324 | var commentType = self.getCommentType(); | |||
r1325 | var resolvesCommentId = self.getResolvesId(); | |||
r1445 | var closePullRequest = self.getClosePr(); | |||
r1 | ||||
if (text === "" && !status) { | ||||
return; | ||||
} | ||||
var excludeCancelBtn = false; | ||||
var submitEvent = true; | ||||
self.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent); | ||||
self.cm.setOption("readOnly", true); | ||||
r1326 | ||||
r1 | var postData = { | |||
'text': text, | ||||
'changeset_status': status, | ||||
r1324 | 'comment_type': commentType, | |||
r1 | 'csrf_token': CSRF_TOKEN | |||
}; | ||||
r1445 | ||||
if (resolvesCommentId) { | ||||
r1325 | postData['resolves_comment_id'] = resolvesCommentId; | |||
} | ||||
r1331 | ||||
r1445 | if (closePullRequest) { | |||
postData['close_pull_request'] = true; | ||||
} | ||||
r1 | var submitSuccessCallback = function(o) { | |||
r1334 | // reload page if we change status for single commit. | |||
if (status && self.commitId) { | ||||
r1 | location.reload(true); | |||
} else { | ||||
$('#injected_page_comments').append(o.rendered_text); | ||||
self.resetCommentFormState(); | ||||
timeagoActivate(); | ||||
r1325 | ||||
r1326 | // mark visually which comment was resolved | |||
r1325 | if (resolvesCommentId) { | |||
r1326 | self.markCommentResolved(resolvesCommentId); | |||
r1325 | } | |||
r1 | } | |||
r1331 | ||||
// run global callback on submit | ||||
self.globalSubmitSuccessCallback(); | ||||
r1 | }; | |||
var submitFailCallback = function(){ | ||||
r1325 | self.resetCommentFormState(text); | |||
r1 | }; | |||
self.submitAjaxPOST( | ||||
self.submitUrl, postData, submitSuccessCallback, submitFailCallback); | ||||
}; | ||||
this.previewSuccessCallback = function(o) { | ||||
$(self.previewBoxSelector).html(o); | ||||
$(self.previewBoxSelector).removeClass('unloaded'); | ||||
r1281 | // swap buttons, making preview active | |||
$(self.previewButton).parent().addClass('active'); | ||||
$(self.editButton).parent().removeClass('active'); | ||||
r1 | ||||
// unlock buttons | ||||
self.setActionButtonsDisabled(false); | ||||
}; | ||||
this.setActionButtonsDisabled = function(state, excludeCancelBtn, submitEvent) { | ||||
excludeCancelBtn = excludeCancelBtn || false; | ||||
submitEvent = submitEvent || false; | ||||
$(this.editButton).prop('disabled', state); | ||||
$(this.previewButton).prop('disabled', state); | ||||
if (!excludeCancelBtn) { | ||||
$(this.cancelButton).prop('disabled', state); | ||||
} | ||||
var submitState = state; | ||||
r1444 | if (!submitEvent && this.getCommentStatus() && !self.isInline()) { | |||
r1 | // if the value of commit review status is set, we allow | |||
r1444 | // submit button, but only on Main form, isInline means inline | |||
r1 | submitState = false | |||
} | ||||
r1445 | ||||
r1 | $(this.submitButton).prop('disabled', submitState); | |||
if (submitEvent) { | ||||
r325 | $(this.submitButton).val(_gettext('Submitting...')); | |||
r1 | } else { | |||
$(this.submitButton).val(this.submitButtonText); | ||||
} | ||||
}; | ||||
// lock preview/edit/submit buttons on load, but exclude cancel button | ||||
var excludeCancelBtn = true; | ||||
this.setActionButtonsDisabled(true, excludeCancelBtn); | ||||
// anonymous users don't have access to initialized CM instance | ||||
if (this.cm !== undefined){ | ||||
this.cm.on('change', function(cMirror) { | ||||
if (cMirror.getValue() === "") { | ||||
self.setActionButtonsDisabled(true, excludeCancelBtn) | ||||
} else { | ||||
self.setActionButtonsDisabled(false, excludeCancelBtn) | ||||
} | ||||
}); | ||||
} | ||||
$(this.editButton).on('click', function(e) { | ||||
e.preventDefault(); | ||||
r1281 | $(self.previewButton).parent().removeClass('active'); | |||
r1 | $(self.previewContainer).hide(); | |||
r1281 | ||||
$(self.editButton).parent().addClass('active'); | ||||
r1 | $(self.editContainer).show(); | |||
}); | ||||
$(this.previewButton).on('click', function(e) { | ||||
e.preventDefault(); | ||||
var text = self.cm.getValue(); | ||||
if (text === "") { | ||||
return; | ||||
} | ||||
var postData = { | ||||
'text': text, | ||||
r1325 | 'renderer': templateContext.visual.default_renderer, | |||
r1 | 'csrf_token': CSRF_TOKEN | |||
}; | ||||
// lock ALL buttons on preview | ||||
self.setActionButtonsDisabled(true); | ||||
$(self.previewBoxSelector).addClass('unloaded'); | ||||
r325 | $(self.previewBoxSelector).html(_gettext('Loading ...')); | |||
r1281 | ||||
r1 | $(self.editContainer).hide(); | |||
$(self.previewContainer).show(); | ||||
// by default we reset state of comment preserving the text | ||||
var previewFailCallback = function(){ | ||||
self.resetCommentFormState(text) | ||||
}; | ||||
self.submitAjaxPOST( | ||||
r1281 | self.previewUrl, postData, self.previewSuccessCallback, | |||
previewFailCallback); | ||||
r1 | ||||
r1281 | $(self.previewButton).parent().addClass('active'); | |||
$(self.editButton).parent().removeClass('active'); | ||||
r1 | }); | |||
$(this.submitForm).submit(function(e) { | ||||
e.preventDefault(); | ||||
var allowedToSubmit = self.isAllowedToSubmit(); | ||||
if (!allowedToSubmit){ | ||||
return false; | ||||
} | ||||
self.handleFormSubmit(); | ||||
}); | ||||
} | ||||
return CommentForm; | ||||
r1325 | }); | |||
r1157 | ||||
r1325 | /* comments controller */ | |||
var CommentsController = function() { | ||||
var mainComment = '#text'; | ||||
r1157 | var self = this; | |||
this.cancelComment = function(node) { | ||||
var $node = $(node); | ||||
var $td = $node.closest('td'); | ||||
r1325 | $node.closest('.comment-inline-form').remove(); | |||
r1157 | return false; | |||
r1192 | }; | |||
r1157 | this.getLineNumber = function(node) { | |||
var $node = $(node); | ||||
return $node.closest('td').attr('data-line-number'); | ||||
r1192 | }; | |||
r1268 | this.scrollToComment = function(node, offset, outdated) { | |||
r1386 | if (offset === undefined) { | |||
offset = 0; | ||||
} | ||||
r1268 | var outdated = outdated || false; | |||
var klass = outdated ? 'div.comment-outdated' : 'div.comment-current'; | ||||
r1157 | if (!node) { | |||
node = $('.comment-selected'); | ||||
if (!node.length) { | ||||
node = $('comment-current') | ||||
} | ||||
} | ||||
r1344 | $wrapper = $(node).closest('div.comment'); | |||
r1268 | $comment = $(node).closest(klass); | |||
$comments = $(klass); | ||||
r1157 | ||||
r1344 | // show hidden comment when referenced. | |||
if (!$wrapper.is(':visible')){ | ||||
$wrapper.show(); | ||||
} | ||||
r1157 | $('.comment-selected').removeClass('comment-selected'); | |||
r1268 | var nextIdx = $(klass).index($comment) + offset; | |||
r1157 | if (nextIdx >= $comments.length) { | |||
nextIdx = 0; | ||||
} | ||||
r1268 | var $next = $(klass).eq(nextIdx); | |||
r1385 | ||||
r1157 | var $cb = $next.closest('.cb'); | |||
r1192 | $cb.removeClass('cb-collapsed'); | |||
r1157 | ||||
var $filediffCollapseState = $cb.closest('.filediff').prev(); | ||||
$filediffCollapseState.prop('checked', false); | ||||
$next.addClass('comment-selected'); | ||||
scrollToElement($next); | ||||
return false; | ||||
r1192 | }; | |||
r1157 | this.nextComment = function(node) { | |||
return self.scrollToComment(node, 1); | ||||
r1192 | }; | |||
r1157 | this.prevComment = function(node) { | |||
return self.scrollToComment(node, -1); | ||||
r1192 | }; | |||
r1268 | this.nextOutdatedComment = function(node) { | |||
return self.scrollToComment(node, 1, true); | ||||
}; | ||||
this.prevOutdatedComment = function(node) { | ||||
return self.scrollToComment(node, -1, true); | ||||
}; | ||||
r1157 | this.deleteComment = function(node) { | |||
if (!confirm(_gettext('Delete this comment?'))) { | ||||
return false; | ||||
} | ||||
var $node = $(node); | ||||
var $td = $node.closest('td'); | ||||
var $comment = $node.closest('.comment'); | ||||
var comment_id = $comment.attr('data-comment-id'); | ||||
var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id); | ||||
var postData = { | ||||
'_method': 'delete', | ||||
'csrf_token': CSRF_TOKEN | ||||
}; | ||||
$comment.addClass('comment-deleting'); | ||||
$comment.hide('fast'); | ||||
var success = function(response) { | ||||
$comment.remove(); | ||||
return false; | ||||
}; | ||||
var failure = function(data, textStatus, xhr) { | ||||
alert("error processing request: " + textStatus); | ||||
$comment.show('fast'); | ||||
$comment.removeClass('comment-deleting'); | ||||
return false; | ||||
}; | ||||
ajaxPOST(url, postData, success, failure); | ||||
r1192 | }; | |||
r1193 | this.toggleWideMode = function (node) { | |||
if ($('#content').hasClass('wrapper')) { | ||||
$('#content').removeClass("wrapper"); | ||||
$('#content').addClass("wide-mode-wrapper"); | ||||
$(node).addClass('btn-success'); | ||||
} else { | ||||
$('#content').removeClass("wide-mode-wrapper"); | ||||
$('#content').addClass("wrapper"); | ||||
$(node).removeClass('btn-success'); | ||||
} | ||||
return false; | ||||
}; | ||||
r1157 | this.toggleComments = function(node, show) { | |||
var $filediff = $(node).closest('.filediff'); | ||||
if (show === true) { | ||||
$filediff.removeClass('hide-comments'); | ||||
} else if (show === false) { | ||||
$filediff.find('.hide-line-comments').removeClass('hide-line-comments'); | ||||
$filediff.addClass('hide-comments'); | ||||
} else { | ||||
$filediff.find('.hide-line-comments').removeClass('hide-line-comments'); | ||||
$filediff.toggleClass('hide-comments'); | ||||
} | ||||
return false; | ||||
r1192 | }; | |||
r1157 | this.toggleLineComments = function(node) { | |||
self.toggleComments(node, true); | ||||
var $node = $(node); | ||||
$node.closest('tr').toggleClass('hide-line-comments'); | ||||
r1192 | }; | |||
r1326 | this.createCommentForm = function(formElement, lineno, placeholderText, initAutocompleteActions, resolvesCommentId){ | |||
var pullRequestId = templateContext.pull_request_data.pull_request_id; | ||||
var commitId = templateContext.commit_data.commit_id; | ||||
var commentForm = new CommentForm( | ||||
formElement, commitId, pullRequestId, lineno, initAutocompleteActions, resolvesCommentId); | ||||
var cm = commentForm.getCmInstance(); | ||||
if (resolvesCommentId){ | ||||
var placeholderText = _gettext('Leave a comment, or click resolve button to resolve TODO comment #{0}').format(resolvesCommentId); | ||||
} | ||||
setTimeout(function() { | ||||
// callbacks | ||||
if (cm !== undefined) { | ||||
commentForm.setPlaceholder(placeholderText); | ||||
if (commentForm.isInline()) { | ||||
cm.focus(); | ||||
cm.refresh(); | ||||
} | ||||
} | ||||
}, 10); | ||||
// trigger scrolldown to the resolve comment, since it might be away | ||||
// from the clicked | ||||
if (resolvesCommentId){ | ||||
var actionNode = $(commentForm.resolvesActionId).offset(); | ||||
setTimeout(function() { | ||||
if (actionNode) { | ||||
$('body, html').animate({scrollTop: actionNode.top}, 10); | ||||
} | ||||
}, 100); | ||||
} | ||||
return commentForm; | ||||
}; | ||||
r1331 | this.createGeneralComment = function (lineNo, placeholderText, resolvesCommentId) { | |||
r1326 | ||||
var tmpl = $('#cb-comment-general-form-template').html(); | ||||
tmpl = tmpl.format(null, 'general'); | ||||
var $form = $(tmpl); | ||||
r1331 | var $formPlaceholder = $('#cb-comment-general-form-placeholder'); | |||
var curForm = $formPlaceholder.find('form'); | ||||
r1326 | if (curForm){ | |||
curForm.remove(); | ||||
} | ||||
r1331 | $formPlaceholder.append($form); | |||
r1326 | ||||
var _form = $($form[0]); | ||||
r1362 | var autocompleteActions = ['approve', 'reject', 'as_note', 'as_todo']; | |||
r1326 | var commentForm = this.createCommentForm( | |||
r1362 | _form, lineNo, placeholderText, autocompleteActions, resolvesCommentId); | |||
r1326 | commentForm.initStatusChangeSelector(); | |||
r1331 | ||||
return commentForm; | ||||
r1326 | }; | |||
r1325 | this.createComment = function(node, resolutionComment) { | |||
var resolvesCommentId = resolutionComment || null; | ||||
r1157 | var $node = $(node); | |||
var $td = $node.closest('td'); | ||||
var $form = $td.find('.comment-inline-form'); | ||||
if (!$form.length) { | ||||
r1326 | ||||
r1157 | var $filediff = $node.closest('.filediff'); | |||
$filediff.removeClass('hide-comments'); | ||||
var f_path = $filediff.attr('data-f-path'); | ||||
var lineno = self.getLineNumber(node); | ||||
r1326 | // create a new HTML from template | |||
var tmpl = $('#cb-comment-inline-form-template').html(); | ||||
r1157 | tmpl = tmpl.format(f_path, lineno); | |||
$form = $(tmpl); | ||||
var $comments = $td.find('.inline-comments'); | ||||
if (!$comments.length) { | ||||
$comments = $( | ||||
$('#cb-comments-inline-container-template').html()); | ||||
$td.append($comments); | ||||
} | ||||
$td.find('.cb-comment-add-button').before($form); | ||||
r1326 | var placeholderText = _gettext('Leave a comment on line {0}.').format(lineno); | |||
r1325 | var _form = $($form[0]).find('form'); | |||
r1362 | var autocompleteActions = ['as_note', 'as_todo']; | |||
r1326 | var commentForm = this.createCommentForm( | |||
r1362 | _form, lineno, placeholderText, autocompleteActions, resolvesCommentId); | |||
r1326 | ||||
$.Topic('/ui/plugins/code/comment_form_built').prepareOrPublish({ | ||||
form: _form, | ||||
parent: $td[0], | ||||
lineno: lineno, | ||||
f_path: f_path} | ||||
); | ||||
r1157 | ||||
// set a CUSTOM submit handler for inline comments. | ||||
commentForm.setHandleFormSubmit(function(o) { | ||||
var text = commentForm.cm.getValue(); | ||||
r1324 | var commentType = commentForm.getCommentType(); | |||
r1325 | var resolvesCommentId = commentForm.getResolvesId(); | |||
r1157 | ||||
if (text === "") { | ||||
return; | ||||
} | ||||
if (lineno === undefined) { | ||||
alert('missing line !'); | ||||
return; | ||||
} | ||||
if (f_path === undefined) { | ||||
alert('missing file path !'); | ||||
return; | ||||
} | ||||
var excludeCancelBtn = false; | ||||
var submitEvent = true; | ||||
commentForm.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent); | ||||
commentForm.cm.setOption("readOnly", true); | ||||
var postData = { | ||||
'text': text, | ||||
'f_path': f_path, | ||||
'line': lineno, | ||||
r1324 | 'comment_type': commentType, | |||
r1157 | 'csrf_token': CSRF_TOKEN | |||
}; | ||||
r1325 | if (resolvesCommentId){ | |||
postData['resolves_comment_id'] = resolvesCommentId; | ||||
} | ||||
r1157 | var submitSuccessCallback = function(json_data) { | |||
$form.remove(); | ||||
try { | ||||
var html = json_data.rendered_text; | ||||
var lineno = json_data.line_no; | ||||
var target_id = json_data.target_id; | ||||
$comments.find('.cb-comment-add-button').before(html); | ||||
r1325 | //mark visually which comment was resolved | |||
if (resolvesCommentId) { | ||||
commentForm.markCommentResolved(resolvesCommentId); | ||||
} | ||||
r1331 | // run global callback on submit | |||
commentForm.globalSubmitSuccessCallback(); | ||||
r1157 | } catch (e) { | |||
console.error(e); | ||||
} | ||||
// re trigger the linkification of next/prev navigation | ||||
linkifyComments($('.inline-comment-injected')); | ||||
timeagoActivate(); | ||||
commentForm.setActionButtonsDisabled(false); | ||||
}; | ||||
var submitFailCallback = function(){ | ||||
commentForm.resetCommentFormState(text) | ||||
}; | ||||
commentForm.submitAjaxPOST( | ||||
commentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback); | ||||
}); | ||||
} | ||||
$form.addClass('comment-inline-form-open'); | ||||
r1192 | }; | |||
r1157 | ||||
r1325 | this.createResolutionComment = function(commentId){ | |||
// hide the trigger text | ||||
$('#resolve-comment-{0}'.format(commentId)).hide(); | ||||
var comment = $('#comment-'+commentId); | ||||
var commentData = comment.data(); | ||||
if (commentData.commentInline) { | ||||
this.createComment(comment, commentId) | ||||
} else { | ||||
r1326 | Rhodecode.comments.createGeneralComment('general', "$placeholder", commentId) | |||
r1325 | } | |||
return false; | ||||
}; | ||||
this.submitResolution = function(commentId){ | ||||
var form = $('#resolve_comment_{0}'.format(commentId)).closest('form'); | ||||
var commentForm = form.get(0).CommentForm; | ||||
var cm = commentForm.getCmInstance(); | ||||
var renderer = templateContext.visual.default_renderer; | ||||
if (renderer == 'rst'){ | ||||
var commentUrl = '`#{0} <{1}#comment-{0}>`_'.format(commentId, commentForm.selfUrl); | ||||
} else if (renderer == 'markdown') { | ||||
var commentUrl = '[#{0}]({1}#comment-{0})'.format(commentId, commentForm.selfUrl); | ||||
} else { | ||||
var commentUrl = '{1}#comment-{0}'.format(commentId, commentForm.selfUrl); | ||||
} | ||||
cm.setValue(_gettext('TODO from comment {0} was fixed.').format(commentUrl)); | ||||
form.submit(); | ||||
return false; | ||||
}; | ||||
r1157 | this.renderInlineComments = function(file_comments) { | |||
show_add_button = typeof show_add_button !== 'undefined' ? show_add_button : true; | ||||
for (var i = 0; i < file_comments.length; i++) { | ||||
var box = file_comments[i]; | ||||
var target_id = $(box).attr('target_id'); | ||||
// actually comments with line numbers | ||||
var comments = box.children; | ||||
for (var j = 0; j < comments.length; j++) { | ||||
var data = { | ||||
'rendered_text': comments[j].outerHTML, | ||||
'line_no': $(comments[j]).attr('line'), | ||||
'target_id': target_id | ||||
}; | ||||
} | ||||
} | ||||
// since order of injection is random, we're now re-iterating | ||||
// from correct order and filling in links | ||||
linkifyComments($('.inline-comment-injected')); | ||||
firefoxAnchorFix(); | ||||
}; | ||||
r1194 | }; | |||