##// END OF EJS Templates
files-source: allow making a range selection with shift on file lines.
ergo -
r2483:aae8d716 default
parent child Browse files
Show More
@@ -1,560 +1,591 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('repo_compare', 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 185 // returns a node from given html;
186 186 var fromHTML = function(html){
187 187 var _html = document.createElement('element');
188 188 _html.innerHTML = html;
189 189 return _html;
190 190 };
191 191
192 192 // Toggle Collapsable Content
193 193 function collapsableContent() {
194 194
195 195 $('.collapsable-content').not('.no-hide').hide();
196 196
197 197 $('.btn-collapse').unbind(); //in case we've been here before
198 198 $('.btn-collapse').click(function() {
199 199 var button = $(this);
200 200 var togglename = $(this).data("toggle");
201 201 $('.collapsable-content[data-toggle='+togglename+']').toggle();
202 202 if ($(this).html()=="Show Less")
203 203 $(this).html("Show More");
204 204 else
205 205 $(this).html("Show Less");
206 206 });
207 207 };
208 208
209 209 var timeagoActivate = function() {
210 210 $("time.timeago").timeago();
211 211 };
212 212
213 213
214 214 var clipboardActivate = function() {
215 215 /*
216 216 *
217 217 * <i class="tooltip icon-plus clipboard-action" data-clipboard-text="${commit.raw_id}" title="${_('Copy the full commit id')}"></i>
218 218 * */
219 219 var clipboard = new Clipboard('.clipboard-action');
220 220
221 221 clipboard.on('success', function(e) {
222 222 var callback = function () {
223 223 $(e.trigger).animate({'opacity': 1.00}, 200)
224 224 };
225 225 $(e.trigger).animate({'opacity': 0.15}, 200, callback);
226 226 e.clearSelection();
227 227 });
228 228 };
229 229
230 230
231 231 // Formatting values in a Select2 dropdown of commit references
232 232 var formatSelect2SelectionRefs = function(commit_ref){
233 233 var tmpl = '';
234 234 if (!commit_ref.text || commit_ref.type === 'sha'){
235 235 return commit_ref.text;
236 236 }
237 237 if (commit_ref.type === 'branch'){
238 238 tmpl = tmpl.concat('<i class="icon-branch"></i> ');
239 239 } else if (commit_ref.type === 'tag'){
240 240 tmpl = tmpl.concat('<i class="icon-tag"></i> ');
241 241 } else if (commit_ref.type === 'book'){
242 242 tmpl = tmpl.concat('<i class="icon-bookmark"></i> ');
243 243 }
244 244 return tmpl.concat(escapeHtml(commit_ref.text));
245 245 };
246 246
247 247 // takes a given html element and scrolls it down offset pixels
248 248 function offsetScroll(element, offset) {
249 249 setTimeout(function() {
250 250 var location = element.offset().top;
251 251 // some browsers use body, some use html
252 252 $('html, body').animate({ scrollTop: (location - offset) });
253 253 }, 100);
254 254 }
255 255
256 256 // scroll an element `percent`% from the top of page in `time` ms
257 257 function scrollToElement(element, percent, time) {
258 258 percent = (percent === undefined ? 25 : percent);
259 259 time = (time === undefined ? 100 : time);
260 260
261 261 var $element = $(element);
262 262 if ($element.length == 0) {
263 263 throw('Cannot scroll to {0}'.format(element))
264 264 }
265 265 var elOffset = $element.offset().top;
266 266 var elHeight = $element.height();
267 267 var windowHeight = $(window).height();
268 268 var offset = elOffset;
269 269 if (elHeight < windowHeight) {
270 270 offset = elOffset - ((windowHeight / (100 / percent)) - (elHeight / 2));
271 271 }
272 272 setTimeout(function() {
273 273 $('html, body').animate({ scrollTop: offset});
274 274 }, time);
275 275 }
276 276
277 277 /**
278 278 * global hooks after DOM is loaded
279 279 */
280 280 $(document).ready(function() {
281 281 firefoxAnchorFix();
282 282
283 283 $('.navigation a.menulink').on('click', function(e){
284 284 var menuitem = $(this).parent('li');
285 285 if (menuitem.hasClass('open')) {
286 286 menuitem.removeClass('open');
287 287 } else {
288 288 menuitem.addClass('open');
289 289 $(document).on('click', function(event) {
290 290 if (!$(event.target).closest(menuitem).length) {
291 291 menuitem.removeClass('open');
292 292 }
293 293 });
294 294 }
295 295 });
296 296 $('.compare_view_files').on(
297 297 'mouseenter mouseleave', 'tr.line .lineno a',function(event) {
298 298 if (event.type === "mouseenter") {
299 299 $(this).parents('tr.line').addClass('hover');
300 300 } else {
301 301 $(this).parents('tr.line').removeClass('hover');
302 302 }
303 303 });
304 304
305 305 $('.compare_view_files').on(
306 306 'mouseenter mouseleave', 'tr.line .add-comment-line a',function(event){
307 307 if (event.type === "mouseenter") {
308 308 $(this).parents('tr.line').addClass('commenting');
309 309 } else {
310 310 $(this).parents('tr.line').removeClass('commenting');
311 311 }
312 312 });
313 313
314 314 $('body').on( /* TODO: replace the $('.compare_view_files').on('click') below
315 315 when new diffs are integrated */
316 316 'click', '.cb-lineno a', function(event) {
317 317
318 if ($(this).attr('data-line-no') !== ""){
318 function sortNumber(a,b) {
319 return a - b;
320 }
321
322 if ($(this).attr('data-line-no') !== "") {
323
324 // on shift, we do a range selection, if we got previous line
325 var prevLine = $('.cb-line-selected a').attr('data-line-no');
326 if (event.shiftKey && prevLine !== undefined) {
327 var prevLine = parseInt(prevLine);
328 var nextLine = parseInt($(this).attr('data-line-no'));
329 var pos = [prevLine, nextLine].sort(sortNumber);
330 var anchor = '#L{0}-{1}'.format(pos[0], pos[1]);
331
332 } else {
333 var nextLine = parseInt($(this).attr('data-line-no'));
334 var pos = [nextLine, nextLine];
335 var anchor = '#L{0}'.format(pos[0]);
336
337 }
338 // highlight
339 var range = [];
340 for (var i = pos[0]; i <= pos[1]; i++) {
341 range.push(i);
342 }
343 // clear selection
319 344 $('.cb-line-selected').removeClass('cb-line-selected');
320 var td = $(this).parent();
321 td.addClass('cb-line-selected'); // line number td
322 td.prev().addClass('cb-line-selected'); // line data td
323 td.next().addClass('cb-line-selected'); // line content td
345
346 $.each(range, function (i, lineNo) {
347 var line_td = $('td.cb-lineno#L' + lineNo);
348 if (line_td.length) {
349 line_td.addClass('cb-line-selected'); // line number td
350 line_td.prev().addClass('cb-line-selected'); // line data
351 line_td.next().addClass('cb-line-selected'); // line content
352 }
353 });
324 354
325 355 // Replace URL without jumping to it if browser supports.
326 356 // Default otherwise
327 357 if (history.pushState) {
328 358 var new_location = location.href.rstrip('#');
329 359 if (location.hash) {
360 // location without hash
330 361 new_location = new_location.replace(location.hash, "");
331 362 }
332 363
333 364 // Make new anchor url
334 new_location = new_location + $(this).attr('href');
365 new_location = new_location + anchor;
335 366 history.pushState(true, document.title, new_location);
336 367
337 368 return false;
338 369 }
339 370 }
340 371 });
341 372
342 373 $('.compare_view_files').on( /* TODO: replace this with .cb function above
343 374 when new diffs are integrated */
344 375 'click', 'tr.line .lineno a',function(event) {
345 376 if ($(this).text() != ""){
346 377 $('tr.line').removeClass('selected');
347 378 $(this).parents("tr.line").addClass('selected');
348 379
349 380 // Replace URL without jumping to it if browser supports.
350 381 // Default otherwise
351 382 if (history.pushState) {
352 383 var new_location = location.href;
353 384 if (location.hash){
354 385 new_location = new_location.replace(location.hash, "");
355 386 }
356 387
357 388 // Make new anchor url
358 389 var new_location = new_location+$(this).attr('href');
359 390 history.pushState(true, document.title, new_location);
360 391
361 392 return false;
362 393 }
363 394 }
364 395 });
365 396
366 397 $('.compare_view_files').on(
367 398 'click', 'tr.line .add-comment-line a',function(event) {
368 399 var tr = $(event.currentTarget).parents('tr.line')[0];
369 400 injectInlineForm(tr);
370 401 return false;
371 402 });
372 403
373 404 $('.collapse_file').on('click', function(e) {
374 405 e.stopPropagation();
375 406 if ($(e.target).is('a')) { return; }
376 407 var node = $(e.delegateTarget).first();
377 408 var icon = $($(node.children().first()).children().first());
378 409 var id = node.attr('fid');
379 410 var target = $('#'+id);
380 411 var tr = $('#tr_'+id);
381 412 var diff = $('#diff_'+id);
382 413 if(node.hasClass('expand_file')){
383 414 node.removeClass('expand_file');
384 415 icon.removeClass('expand_file_icon');
385 416 node.addClass('collapse_file');
386 417 icon.addClass('collapse_file_icon');
387 418 diff.show();
388 419 tr.show();
389 420 target.show();
390 421 } else {
391 422 node.removeClass('collapse_file');
392 423 icon.removeClass('collapse_file_icon');
393 424 node.addClass('expand_file');
394 425 icon.addClass('expand_file_icon');
395 426 diff.hide();
396 427 tr.hide();
397 428 target.hide();
398 429 }
399 430 });
400 431
401 432 $('#expand_all_files').click(function() {
402 433 $('.expand_file').each(function() {
403 434 var node = $(this);
404 435 var icon = $($(node.children().first()).children().first());
405 436 var id = $(this).attr('fid');
406 437 var target = $('#'+id);
407 438 var tr = $('#tr_'+id);
408 439 var diff = $('#diff_'+id);
409 440 node.removeClass('expand_file');
410 441 icon.removeClass('expand_file_icon');
411 442 node.addClass('collapse_file');
412 443 icon.addClass('collapse_file_icon');
413 444 diff.show();
414 445 tr.show();
415 446 target.show();
416 447 });
417 448 });
418 449
419 450 $('#collapse_all_files').click(function() {
420 451 $('.collapse_file').each(function() {
421 452 var node = $(this);
422 453 var icon = $($(node.children().first()).children().first());
423 454 var id = $(this).attr('fid');
424 455 var target = $('#'+id);
425 456 var tr = $('#tr_'+id);
426 457 var diff = $('#diff_'+id);
427 458 node.removeClass('collapse_file');
428 459 icon.removeClass('collapse_file_icon');
429 460 node.addClass('expand_file');
430 461 icon.addClass('expand_file_icon');
431 462 diff.hide();
432 463 tr.hide();
433 464 target.hide();
434 465 });
435 466 });
436 467
437 468 // Mouse over behavior for comments and line selection
438 469
439 470 // Select the line that comes from the url anchor
440 471 // At the time of development, Chrome didn't seem to support jquery's :target
441 472 // element, so I had to scroll manually
442 473
443 474 if (location.hash) {
444 475 var result = splitDelimitedHash(location.hash);
445 476 var loc = result.loc;
446 477 if (loc.length > 1) {
447 478
448 479 var highlightable_line_tds = [];
449 480
450 481 // source code line format
451 482 var page_highlights = loc.substring(
452 483 loc.indexOf('#') + 1).split('L');
453 484
454 485 if (page_highlights.length > 1) {
455 486 var highlight_ranges = page_highlights[1].split(",");
456 487 var h_lines = [];
457 488 for (var pos in highlight_ranges) {
458 489 var _range = highlight_ranges[pos].split('-');
459 490 if (_range.length === 2) {
460 491 var start = parseInt(_range[0]);
461 492 var end = parseInt(_range[1]);
462 493 if (start < end) {
463 494 for (var i = start; i <= end; i++) {
464 495 h_lines.push(i);
465 496 }
466 497 }
467 498 }
468 499 else {
469 500 h_lines.push(parseInt(highlight_ranges[pos]));
470 501 }
471 502 }
472 503 for (pos in h_lines) {
473 504 var line_td = $('td.cb-lineno#L' + h_lines[pos]);
474 505 if (line_td.length) {
475 506 highlightable_line_tds.push(line_td);
476 507 }
477 508 }
478 509 }
479 510
480 511 // now check a direct id reference (diff page)
481 512 if ($(loc).length && $(loc).hasClass('cb-lineno')) {
482 513 highlightable_line_tds.push($(loc));
483 514 }
484 515 $.each(highlightable_line_tds, function (i, $td) {
485 516 $td.addClass('cb-line-selected'); // line number td
486 517 $td.prev().addClass('cb-line-selected'); // line data
487 518 $td.next().addClass('cb-line-selected'); // line content
488 519 });
489 520
490 521 if (highlightable_line_tds.length) {
491 522 var $first_line_td = highlightable_line_tds[0];
492 523 scrollToElement($first_line_td);
493 524 $.Topic('/ui/plugins/code/anchor_focus').prepareOrPublish({
494 525 td: $first_line_td,
495 526 remainder: result.remainder
496 527 });
497 528 }
498 529 }
499 530 }
500 531 collapsableContent();
501 532 });
502 533
503 534 var feedLifetimeOptions = function(query, initialData){
504 535 var data = {results: []};
505 536 var isQuery = typeof query.term !== 'undefined';
506 537
507 538 var section = _gettext('Lifetime');
508 539 var children = [];
509 540
510 541 //filter results
511 542 $.each(initialData.results, function(idx, value) {
512 543
513 544 if (!isQuery || query.term.length === 0 || value.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0) {
514 545 children.push({
515 546 'id': this.id,
516 547 'text': this.text
517 548 })
518 549 }
519 550
520 551 });
521 552 data.results.push({
522 553 'text': section,
523 554 'children': children
524 555 });
525 556
526 557 if (isQuery) {
527 558
528 559 var now = moment.utc();
529 560
530 561 var parseQuery = function(entry, now){
531 562 var fmt = 'DD/MM/YYYY H:mm';
532 563 var parsed = moment.utc(entry, fmt);
533 564 var diffInMin = parsed.diff(now, 'minutes');
534 565
535 566 if (diffInMin > 0){
536 567 return {
537 568 id: diffInMin,
538 569 text: parsed.format(fmt)
539 570 }
540 571 } else {
541 572 return {
542 573 id: undefined,
543 574 text: parsed.format('DD/MM/YYYY') + ' ' + _gettext('date not in future')
544 575 }
545 576 }
546 577
547 578
548 579 };
549 580
550 581 data.results.push({
551 582 'text': _gettext('Specified expiration date'),
552 583 'children': [{
553 584 'id': parseQuery(query.term, now).id,
554 585 'text': parseQuery(query.term, now).text
555 586 }]
556 587 });
557 588 }
558 589
559 590 query.callback(data);
560 591 };
General Comments 0
You need to be logged in to leave comments. Login now