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