##// END OF EJS Templates
diffs: added scroll down/scroll up helper. Fixes #5643
marcink -
r4595:82d4afd7 stable
parent child Browse files
Show More
@@ -1,626 +1,630 b''
1
1
2
2
3 //BUTTONS
3 //BUTTONS
4 button,
4 button,
5 .btn,
5 .btn,
6 input[type="button"] {
6 input[type="button"] {
7 -webkit-appearance: none;
7 -webkit-appearance: none;
8 display: inline-block;
8 display: inline-block;
9 margin: 0 @padding/3 0 0;
9 margin: 0 @padding/3 0 0;
10 padding: @button-padding;
10 padding: @button-padding;
11 text-align: center;
11 text-align: center;
12 font-size: @basefontsize;
12 font-size: @basefontsize;
13 line-height: 1em;
13 line-height: 1em;
14 font-family: @text-light;
14 font-family: @text-light;
15 text-decoration: none;
15 text-decoration: none;
16 text-shadow: none;
16 text-shadow: none;
17 color: @grey2;
17 color: @grey2;
18 background-color: white;
18 background-color: white;
19 background-image: none;
19 background-image: none;
20 border: none;
20 border: none;
21 .border ( @border-thickness-buttons, @grey5 );
21 .border ( @border-thickness-buttons, @grey5 );
22 .border-radius (@border-radius);
22 .border-radius (@border-radius);
23 cursor: pointer;
23 cursor: pointer;
24 white-space: nowrap;
24 white-space: nowrap;
25 -webkit-transition: background .3s,color .3s;
25 -webkit-transition: background .3s,color .3s;
26 -moz-transition: background .3s,color .3s;
26 -moz-transition: background .3s,color .3s;
27 -o-transition: background .3s,color .3s;
27 -o-transition: background .3s,color .3s;
28 transition: background .3s,color .3s;
28 transition: background .3s,color .3s;
29 box-shadow: @button-shadow;
29 box-shadow: @button-shadow;
30 -webkit-box-shadow: @button-shadow;
30 -webkit-box-shadow: @button-shadow;
31
31
32
32
33
33
34 a {
34 a {
35 display: block;
35 display: block;
36 margin: 0;
36 margin: 0;
37 padding: 0;
37 padding: 0;
38 color: inherit;
38 color: inherit;
39 text-decoration: none;
39 text-decoration: none;
40
40
41 &:hover {
41 &:hover {
42 text-decoration: none;
42 text-decoration: none;
43 }
43 }
44 }
44 }
45
45
46 &:focus,
46 &:focus,
47 &:active {
47 &:active {
48 outline:none;
48 outline:none;
49 }
49 }
50
50
51 &:hover {
51 &:hover {
52 color: @rcdarkblue;
52 color: @rcdarkblue;
53 background-color: @grey6;
53 background-color: @grey6;
54
54
55 }
55 }
56
56
57 &.btn-active {
57 &.btn-active {
58 color: @rcdarkblue;
58 color: @rcdarkblue;
59 background-color: @grey6;
59 background-color: @grey6;
60 }
60 }
61
61
62 .icon-remove {
62 .icon-remove {
63 display: none;
63 display: none;
64 }
64 }
65
65
66 //disabled buttons
66 //disabled buttons
67 //last; overrides any other styles
67 //last; overrides any other styles
68 &:disabled {
68 &:disabled {
69 opacity: .7;
69 opacity: .7;
70 cursor: auto;
70 cursor: auto;
71 background-color: white;
71 background-color: white;
72 color: @grey4;
72 color: @grey4;
73 text-shadow: none;
73 text-shadow: none;
74 }
74 }
75
75
76 &.no-margin {
76 &.no-margin {
77 margin: 0 0 0 0;
77 margin: 0 0 0 0;
78 }
78 }
79
79
80
80
81
81
82 }
82 }
83
83
84
84
85 .btn-default {
85 .btn-default {
86 border: @border-thickness solid @grey5;
86 border: @border-thickness solid @grey5;
87 background-image: none;
87 background-image: none;
88 color: @grey2;
88 color: @grey2;
89
89
90 a {
90 a {
91 color: @grey2;
91 color: @grey2;
92 }
92 }
93
93
94 &:hover,
94 &:hover,
95 &.active {
95 &.active {
96 color: @rcdarkblue;
96 color: @rcdarkblue;
97 background-color: @white;
97 background-color: @white;
98 .border ( @border-thickness, @grey4 );
98 .border ( @border-thickness, @grey4 );
99
99
100 a {
100 a {
101 color: @grey2;
101 color: @grey2;
102 }
102 }
103 }
103 }
104 &:disabled {
104 &:disabled {
105 .border ( @border-thickness-buttons, @grey5 );
105 .border ( @border-thickness-buttons, @grey5 );
106 background-color: transparent;
106 background-color: transparent;
107 }
107 }
108 &.btn-active {
108 &.btn-active {
109 color: @rcdarkblue;
109 color: @rcdarkblue;
110 background-color: @white;
110 background-color: @white;
111 .border ( @border-thickness, @rcdarkblue );
111 .border ( @border-thickness, @rcdarkblue );
112 }
112 }
113 }
113 }
114
114
115 .btn-primary,
115 .btn-primary,
116 .btn-small, /* TODO: anderson: remove .btn-small to not mix with the new btn-sm */
116 .btn-small, /* TODO: anderson: remove .btn-small to not mix with the new btn-sm */
117 .btn-success {
117 .btn-success {
118 .border ( @border-thickness, @rcblue );
118 .border ( @border-thickness, @rcblue );
119 background-color: @rcblue;
119 background-color: @rcblue;
120 color: white;
120 color: white;
121
121
122 a {
122 a {
123 color: white;
123 color: white;
124 }
124 }
125
125
126 &:hover,
126 &:hover,
127 &.active {
127 &.active {
128 .border ( @border-thickness, @rcdarkblue );
128 .border ( @border-thickness, @rcdarkblue );
129 color: white;
129 color: white;
130 background-color: @rcdarkblue;
130 background-color: @rcdarkblue;
131
131
132 a {
132 a {
133 color: white;
133 color: white;
134 }
134 }
135 }
135 }
136 &:disabled {
136 &:disabled {
137 background-color: @rcblue;
137 background-color: @rcblue;
138 }
138 }
139 }
139 }
140
140
141 .btn-secondary {
141 .btn-secondary {
142 &:extend(.btn-default);
142 &:extend(.btn-default);
143
143
144 background-color: white;
144 background-color: white;
145
145
146 &:focus {
146 &:focus {
147 outline: 0;
147 outline: 0;
148 }
148 }
149
149
150 &:hover {
150 &:hover {
151 &:extend(.btn-default:hover);
151 &:extend(.btn-default:hover);
152 }
152 }
153
153
154 &.btn-link {
154 &.btn-link {
155 &:extend(.btn-link);
155 &:extend(.btn-link);
156 color: @rcblue;
156 color: @rcblue;
157 }
157 }
158
158
159 &:disabled {
159 &:disabled {
160 color: @rcblue;
160 color: @rcblue;
161 background-color: white;
161 background-color: white;
162 }
162 }
163 }
163 }
164
164
165 .btn-danger,
165 .btn-danger,
166 .revoke_perm,
166 .revoke_perm,
167 .btn-x,
167 .btn-x,
168 .form .action_button.btn-x {
168 .form .action_button.btn-x {
169 .border ( @border-thickness, @alert2 );
169 .border ( @border-thickness, @alert2 );
170 background-color: white;
170 background-color: white;
171 color: @alert2;
171 color: @alert2;
172
172
173 a {
173 a {
174 color: @alert2;
174 color: @alert2;
175 }
175 }
176
176
177 &:hover,
177 &:hover,
178 &.active {
178 &.active {
179 .border ( @border-thickness, @alert2 );
179 .border ( @border-thickness, @alert2 );
180 color: white;
180 color: white;
181 background-color: @alert2;
181 background-color: @alert2;
182
182
183 a {
183 a {
184 color: white;
184 color: white;
185 }
185 }
186 }
186 }
187
187
188 i {
188 i {
189 display:none;
189 display:none;
190 }
190 }
191
191
192 &:disabled {
192 &:disabled {
193 background-color: white;
193 background-color: white;
194 color: @alert2;
194 color: @alert2;
195 }
195 }
196 }
196 }
197
197
198 .btn-warning {
198 .btn-warning {
199 .border ( @border-thickness, @alert3 );
199 .border ( @border-thickness, @alert3 );
200 background-color: white;
200 background-color: white;
201 color: @alert3;
201 color: @alert3;
202
202
203 a {
203 a {
204 color: @alert3;
204 color: @alert3;
205 }
205 }
206
206
207 &:hover,
207 &:hover,
208 &.active {
208 &.active {
209 .border ( @border-thickness, @alert3 );
209 .border ( @border-thickness, @alert3 );
210 color: white;
210 color: white;
211 background-color: @alert3;
211 background-color: @alert3;
212
212
213 a {
213 a {
214 color: white;
214 color: white;
215 }
215 }
216 }
216 }
217
217
218 i {
218 i {
219 display:none;
219 display:none;
220 }
220 }
221
221
222 &:disabled {
222 &:disabled {
223 background-color: white;
223 background-color: white;
224 color: @alert3;
224 color: @alert3;
225 }
225 }
226 }
226 }
227
227
228
228
229 .btn-approved-status {
229 .btn-approved-status {
230 .border ( @border-thickness, @alert1 );
230 .border ( @border-thickness, @alert1 );
231 background-color: white;
231 background-color: white;
232 color: @alert1;
232 color: @alert1;
233
233
234 }
234 }
235
235
236 .btn-rejected-status {
236 .btn-rejected-status {
237 .border ( @border-thickness, @alert2 );
237 .border ( @border-thickness, @alert2 );
238 background-color: white;
238 background-color: white;
239 color: @alert2;
239 color: @alert2;
240 }
240 }
241
241
242 .btn-sm,
242 .btn-sm,
243 .btn-mini,
243 .btn-mini,
244 .field-sm .btn {
244 .field-sm .btn {
245 padding: @padding/3;
245 padding: @padding/3;
246 }
246 }
247
247
248 .btn-xs {
248 .btn-xs {
249 padding: @padding/4;
249 padding: @padding/4;
250 }
250 }
251
251
252 .btn-lg {
252 .btn-lg {
253 padding: @padding * 1.2;
253 padding: @padding * 1.2;
254 }
254 }
255
255
256 .btn-group {
256 .btn-group {
257 display: inline-block;
257 display: inline-block;
258 .btn {
258 .btn {
259 float: left;
259 float: left;
260 margin: 0 0 0 0;
260 margin: 0 0 0 0;
261 // first item
261 // first item
262 &:first-of-type:not(:last-of-type) {
262 &:first-of-type:not(:last-of-type) {
263 border-radius: @border-radius 0 0 @border-radius;
263 border-radius: @border-radius 0 0 @border-radius;
264
264
265 }
265 }
266 // 2nd, if only 2 elements are there
267 &:nth-of-type(2) {
268 border-left-width: 0;
269 }
266 // middle elements
270 // middle elements
267 &:not(:first-of-type):not(:last-of-type) {
271 &:not(:first-of-type):not(:last-of-type) {
268 border-radius: 0;
272 border-radius: 0;
269 border-left-width: 0;
273 border-left-width: 0;
270 border-right-width: 0;
274 border-right-width: 0;
271 }
275 }
272 // last item
276 // last item
273 &:last-of-type:not(:first-of-type) {
277 &:last-of-type:not(:first-of-type) {
274 border-radius: 0 @border-radius @border-radius 0;
278 border-radius: 0 @border-radius @border-radius 0;
275 }
279 }
276
280
277 &:only-child {
281 &:only-child {
278 border-radius: @border-radius;
282 border-radius: @border-radius;
279 }
283 }
280 }
284 }
281
285
282 }
286 }
283
287
284
288
285 .btn-group-actions {
289 .btn-group-actions {
286 position: relative;
290 position: relative;
287 z-index: 50;
291 z-index: 50;
288
292
289 &:not(.open) .btn-action-switcher-container {
293 &:not(.open) .btn-action-switcher-container {
290 display: none;
294 display: none;
291 }
295 }
292
296
293 .btn-more-option {
297 .btn-more-option {
294 margin-left: -1px;
298 margin-left: -1px;
295 padding-left: 2px;
299 padding-left: 2px;
296 padding-right: 2px;
300 padding-right: 2px;
297 }
301 }
298 }
302 }
299
303
300
304
301 .btn-action-switcher-container {
305 .btn-action-switcher-container {
302 position: absolute;
306 position: absolute;
303 top: 100%;
307 top: 100%;
304
308
305 &.left-align {
309 &.left-align {
306 left: 0;
310 left: 0;
307 }
311 }
308 &.right-align {
312 &.right-align {
309 right: 0;
313 right: 0;
310 }
314 }
311
315
312 }
316 }
313
317
314 .btn-action-switcher {
318 .btn-action-switcher {
315 display: block;
319 display: block;
316 position: relative;
320 position: relative;
317 z-index: 300;
321 z-index: 300;
318 max-width: 600px;
322 max-width: 600px;
319 margin-top: 4px;
323 margin-top: 4px;
320 margin-bottom: 24px;
324 margin-bottom: 24px;
321 font-size: 14px;
325 font-size: 14px;
322 font-weight: 400;
326 font-weight: 400;
323 padding: 8px 0;
327 padding: 8px 0;
324 background-color: #fff;
328 background-color: #fff;
325 border: 1px solid @grey4;
329 border: 1px solid @grey4;
326 border-radius: 3px;
330 border-radius: 3px;
327 box-shadow: @dropdown-shadow;
331 box-shadow: @dropdown-shadow;
328 overflow: auto;
332 overflow: auto;
329
333
330 li {
334 li {
331 display: block;
335 display: block;
332 text-align: left;
336 text-align: left;
333 list-style: none;
337 list-style: none;
334 padding: 5px 10px;
338 padding: 5px 10px;
335 }
339 }
336
340
337 li .action-help-block {
341 li .action-help-block {
338 font-size: 10px;
342 font-size: 10px;
339 line-height: normal;
343 line-height: normal;
340 color: @grey4;
344 color: @grey4;
341 }
345 }
342
346
343 }
347 }
344
348
345 .btn-link {
349 .btn-link {
346 background: transparent;
350 background: transparent;
347 border: none;
351 border: none;
348 padding: 0;
352 padding: 0;
349 color: @rcblue;
353 color: @rcblue;
350
354
351 &:hover {
355 &:hover {
352 background: transparent;
356 background: transparent;
353 border: none;
357 border: none;
354 color: @rcdarkblue;
358 color: @rcdarkblue;
355 }
359 }
356
360
357 //disabled buttons
361 //disabled buttons
358 //last; overrides any other styles
362 //last; overrides any other styles
359 &:disabled {
363 &:disabled {
360 opacity: .7;
364 opacity: .7;
361 cursor: auto;
365 cursor: auto;
362 background-color: white;
366 background-color: white;
363 color: @grey4;
367 color: @grey4;
364 text-shadow: none;
368 text-shadow: none;
365 }
369 }
366
370
367 // TODO: johbo: Check if we can avoid this, indicates that the structure
371 // TODO: johbo: Check if we can avoid this, indicates that the structure
368 // is not yet good.
372 // is not yet good.
369 // lisa: The button CSS reflects the button HTML; both need a cleanup.
373 // lisa: The button CSS reflects the button HTML; both need a cleanup.
370 &.btn-danger {
374 &.btn-danger {
371 color: @alert2;
375 color: @alert2;
372
376
373 &:hover {
377 &:hover {
374 color: darken(@alert2, 30%);
378 color: darken(@alert2, 30%);
375 }
379 }
376
380
377 &:disabled {
381 &:disabled {
378 color: @alert2;
382 color: @alert2;
379 }
383 }
380 }
384 }
381 }
385 }
382
386
383 .btn-social {
387 .btn-social {
384 &:extend(.btn-default);
388 &:extend(.btn-default);
385 margin: 5px 5px 5px 0px;
389 margin: 5px 5px 5px 0px;
386 min-width: 160px;
390 min-width: 160px;
387 }
391 }
388
392
389 // TODO: johbo: check these exceptions
393 // TODO: johbo: check these exceptions
390
394
391 .links {
395 .links {
392
396
393 .btn + .btn {
397 .btn + .btn {
394 margin-top: @padding;
398 margin-top: @padding;
395 }
399 }
396 }
400 }
397
401
398
402
399 .action_button {
403 .action_button {
400 display:inline;
404 display:inline;
401 margin: 0;
405 margin: 0;
402 padding: 0 1em 0 0;
406 padding: 0 1em 0 0;
403 font-size: inherit;
407 font-size: inherit;
404 color: @rcblue;
408 color: @rcblue;
405 border: none;
409 border: none;
406 border-radius: 0;
410 border-radius: 0;
407 background-color: transparent;
411 background-color: transparent;
408
412
409 &.last-item {
413 &.last-item {
410 border: none;
414 border: none;
411 padding: 0 0 0 0;
415 padding: 0 0 0 0;
412 }
416 }
413
417
414 &:last-child {
418 &:last-child {
415 border: none;
419 border: none;
416 padding: 0 0 0 0;
420 padding: 0 0 0 0;
417 }
421 }
418
422
419 &:hover {
423 &:hover {
420 color: @rcdarkblue;
424 color: @rcdarkblue;
421 background-color: transparent;
425 background-color: transparent;
422 border: none;
426 border: none;
423 }
427 }
424 .noselect
428 .noselect
425 }
429 }
426
430
427 .grid_delete {
431 .grid_delete {
428 .action_button {
432 .action_button {
429 border: none;
433 border: none;
430 }
434 }
431 }
435 }
432
436
433 input[type="submit"].btn-draft {
437 input[type="submit"].btn-draft {
434 .border ( @border-thickness, @rcblue );
438 .border ( @border-thickness, @rcblue );
435 background-color: white;
439 background-color: white;
436 color: @rcblue;
440 color: @rcblue;
437
441
438 a {
442 a {
439 color: @rcblue;
443 color: @rcblue;
440 }
444 }
441
445
442 &:hover,
446 &:hover,
443 &.active {
447 &.active {
444 .border ( @border-thickness, @rcdarkblue );
448 .border ( @border-thickness, @rcdarkblue );
445 background-color: white;
449 background-color: white;
446 color: @rcdarkblue;
450 color: @rcdarkblue;
447
451
448 a {
452 a {
449 color: @rcdarkblue;
453 color: @rcdarkblue;
450 }
454 }
451 }
455 }
452
456
453 &:disabled {
457 &:disabled {
454 background-color: white;
458 background-color: white;
455 color: @rcblue;
459 color: @rcblue;
456 }
460 }
457 }
461 }
458
462
459 input[type="submit"].btn-warning {
463 input[type="submit"].btn-warning {
460 &:extend(.btn-warning);
464 &:extend(.btn-warning);
461
465
462 &:focus {
466 &:focus {
463 outline: 0;
467 outline: 0;
464 }
468 }
465
469
466 &:hover {
470 &:hover {
467 &:extend(.btn-warning:hover);
471 &:extend(.btn-warning:hover);
468 }
472 }
469
473
470 &.btn-link {
474 &.btn-link {
471 &:extend(.btn-link);
475 &:extend(.btn-link);
472 color: @alert3;
476 color: @alert3;
473
477
474 &:disabled {
478 &:disabled {
475 color: @alert3;
479 color: @alert3;
476 background-color: transparent;
480 background-color: transparent;
477 }
481 }
478 }
482 }
479
483
480 &:disabled {
484 &:disabled {
481 .border ( @border-thickness-buttons, @alert3 );
485 .border ( @border-thickness-buttons, @alert3 );
482 background-color: white;
486 background-color: white;
483 color: @alert3;
487 color: @alert3;
484 opacity: 0.5;
488 opacity: 0.5;
485 }
489 }
486 }
490 }
487
491
488
492
489
493
490 // TODO: johbo: Form button tweaks, check if we can use the classes instead
494 // TODO: johbo: Form button tweaks, check if we can use the classes instead
491 input[type="submit"] {
495 input[type="submit"] {
492 &:extend(.btn-primary);
496 &:extend(.btn-primary);
493
497
494 &:focus {
498 &:focus {
495 outline: 0;
499 outline: 0;
496 }
500 }
497
501
498 &:hover {
502 &:hover {
499 &:extend(.btn-primary:hover);
503 &:extend(.btn-primary:hover);
500 }
504 }
501
505
502 &.btn-link {
506 &.btn-link {
503 &:extend(.btn-link);
507 &:extend(.btn-link);
504 color: @rcblue;
508 color: @rcblue;
505
509
506 &:disabled {
510 &:disabled {
507 color: @rcblue;
511 color: @rcblue;
508 background-color: transparent;
512 background-color: transparent;
509 }
513 }
510 }
514 }
511
515
512 &:disabled {
516 &:disabled {
513 .border ( @border-thickness-buttons, @rcblue );
517 .border ( @border-thickness-buttons, @rcblue );
514 background-color: @rcblue;
518 background-color: @rcblue;
515 color: white;
519 color: white;
516 opacity: 0.5;
520 opacity: 0.5;
517 }
521 }
518 }
522 }
519
523
520 input[type="reset"] {
524 input[type="reset"] {
521 &:extend(.btn-default);
525 &:extend(.btn-default);
522
526
523 // TODO: johbo: Check if this tweak can be avoided.
527 // TODO: johbo: Check if this tweak can be avoided.
524 background: transparent;
528 background: transparent;
525
529
526 &:focus {
530 &:focus {
527 outline: 0;
531 outline: 0;
528 }
532 }
529
533
530 &:hover {
534 &:hover {
531 &:extend(.btn-default:hover);
535 &:extend(.btn-default:hover);
532 }
536 }
533
537
534 &.btn-link {
538 &.btn-link {
535 &:extend(.btn-link);
539 &:extend(.btn-link);
536 color: @rcblue;
540 color: @rcblue;
537
541
538 &:disabled {
542 &:disabled {
539 border: none;
543 border: none;
540 }
544 }
541 }
545 }
542
546
543 &:disabled {
547 &:disabled {
544 .border ( @border-thickness-buttons, @rcblue );
548 .border ( @border-thickness-buttons, @rcblue );
545 background-color: white;
549 background-color: white;
546 color: @rcblue;
550 color: @rcblue;
547 }
551 }
548 }
552 }
549
553
550 input[type="submit"],
554 input[type="submit"],
551 input[type="reset"] {
555 input[type="reset"] {
552 &.btn-danger {
556 &.btn-danger {
553 &:extend(.btn-danger);
557 &:extend(.btn-danger);
554
558
555 &:focus {
559 &:focus {
556 outline: 0;
560 outline: 0;
557 }
561 }
558
562
559 &:hover {
563 &:hover {
560 &:extend(.btn-danger:hover);
564 &:extend(.btn-danger:hover);
561 }
565 }
562
566
563 &.btn-link {
567 &.btn-link {
564 &:extend(.btn-link);
568 &:extend(.btn-link);
565 color: @alert2;
569 color: @alert2;
566
570
567 &:hover {
571 &:hover {
568 color: darken(@alert2,30%);
572 color: darken(@alert2,30%);
569 }
573 }
570 }
574 }
571
575
572 &:disabled {
576 &:disabled {
573 color: @alert2;
577 color: @alert2;
574 background-color: white;
578 background-color: white;
575 }
579 }
576 }
580 }
577 &.btn-danger-action {
581 &.btn-danger-action {
578 .border ( @border-thickness, @alert2 );
582 .border ( @border-thickness, @alert2 );
579 background-color: @alert2;
583 background-color: @alert2;
580 color: white;
584 color: white;
581
585
582 a {
586 a {
583 color: white;
587 color: white;
584 }
588 }
585
589
586 &:hover {
590 &:hover {
587 background-color: darken(@alert2,20%);
591 background-color: darken(@alert2,20%);
588 }
592 }
589
593
590 &.active {
594 &.active {
591 .border ( @border-thickness, @alert2 );
595 .border ( @border-thickness, @alert2 );
592 color: white;
596 color: white;
593 background-color: @alert2;
597 background-color: @alert2;
594
598
595 a {
599 a {
596 color: white;
600 color: white;
597 }
601 }
598 }
602 }
599
603
600 &:disabled {
604 &:disabled {
601 background-color: white;
605 background-color: white;
602 color: @alert2;
606 color: @alert2;
603 }
607 }
604 }
608 }
605 }
609 }
606
610
607
611
608 .button-links {
612 .button-links {
609 float: left;
613 float: left;
610 display: inline;
614 display: inline;
611 margin: 0;
615 margin: 0;
612 padding-left: 0;
616 padding-left: 0;
613 list-style: none;
617 list-style: none;
614 text-align: right;
618 text-align: right;
615
619
616 li {
620 li {
617
621
618
622
619 }
623 }
620
624
621 li.active {
625 li.active {
622 background-color: @grey6;
626 background-color: @grey6;
623 .border ( @border-thickness, @grey4 );
627 .border ( @border-thickness, @grey4 );
624 }
628 }
625
629
626 }
630 }
@@ -1,293 +1,297 b''
1 @font-face {
1 @font-face {
2 font-family: 'rcicons';
2 font-family: 'rcicons';
3
3
4 src: url('../fonts/RCIcons/rcicons.eot?44705679');
4 src: url('../fonts/RCIcons/rcicons.eot?44705679');
5 src: url('../fonts/RCIcons/rcicons.eot?44705679#iefix') format('embedded-opentype'),
5 src: url('../fonts/RCIcons/rcicons.eot?44705679#iefix') format('embedded-opentype'),
6 url('../fonts/RCIcons/rcicons.woff2?44705679') format('woff2'),
6 url('../fonts/RCIcons/rcicons.woff2?44705679') format('woff2'),
7 url('../fonts/RCIcons/rcicons.woff?44705679') format('woff'),
7 url('../fonts/RCIcons/rcicons.woff?44705679') format('woff'),
8 url('../fonts/RCIcons/rcicons.ttf?44705679') format('truetype'),
8 url('../fonts/RCIcons/rcicons.ttf?44705679') format('truetype'),
9 url('../fonts/RCIcons/rcicons.svg?44705679#rcicons') format('svg');
9 url('../fonts/RCIcons/rcicons.svg?44705679#rcicons') format('svg');
10
10
11 font-weight: normal;
11 font-weight: normal;
12 font-style: normal;
12 font-style: normal;
13 }
13 }
14 /* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */
14 /* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */
15 /* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */
15 /* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */
16 /*
16 /*
17 @media screen and (-webkit-min-device-pixel-ratio:0) {
17 @media screen and (-webkit-min-device-pixel-ratio:0) {
18 @font-face {
18 @font-face {
19 font-family: 'rcicons';
19 font-family: 'rcicons';
20 src: url('../fonts/RCIcons/rcicons.svg?74666722#rcicons') format('svg');
20 src: url('../fonts/RCIcons/rcicons.svg?74666722#rcicons') format('svg');
21 }
21 }
22 }
22 }
23 */
23 */
24
24
25 [class^="icon-"]:before, [class*=" icon-"]:before {
25 [class^="icon-"]:before, [class*=" icon-"]:before {
26 font-family: "rcicons";
26 font-family: "rcicons";
27 font-style: normal;
27 font-style: normal;
28 font-weight: normal;
28 font-weight: normal;
29 speak: none;
29 speak: none;
30
30
31 display: inline-block;
31 display: inline-block;
32 text-decoration: inherit;
32 text-decoration: inherit;
33 width: 1em;
33 width: 1em;
34 margin-right: .2em;
34 margin-right: .2em;
35 text-align: center;
35 text-align: center;
36 /* opacity: .8; */
36 /* opacity: .8; */
37
37
38 /* For safety - reset parent styles, that can break glyph codes*/
38 /* For safety - reset parent styles, that can break glyph codes*/
39 font-variant: normal;
39 font-variant: normal;
40 text-transform: none;
40 text-transform: none;
41
41
42 /* fix buttons height, for twitter bootstrap */
42 /* fix buttons height, for twitter bootstrap */
43 line-height: 1em;
43 line-height: 1em;
44
44
45 /* Animation center compensation - margins should be symmetric */
45 /* Animation center compensation - margins should be symmetric */
46 /* remove if not needed */
46 /* remove if not needed */
47 margin-left: .2em;
47 margin-left: .2em;
48
48
49 /* you can be more comfortable with increased icons size */
49 /* you can be more comfortable with increased icons size */
50 /* font-size: 120%; */
50 /* font-size: 120%; */
51
51
52 /* Font smoothing. That was taken from TWBS */
52 /* Font smoothing. That was taken from TWBS */
53 -webkit-font-smoothing: antialiased;
53 -webkit-font-smoothing: antialiased;
54 -moz-osx-font-smoothing: grayscale;
54 -moz-osx-font-smoothing: grayscale;
55
55
56 /* Uncomment for 3D effect */
56 /* Uncomment for 3D effect */
57 /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */
57 /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */
58 }
58 }
59
59
60 .animate-spin {
60 .animate-spin {
61 -moz-animation: spin 2s infinite linear;
61 -moz-animation: spin 2s infinite linear;
62 -o-animation: spin 2s infinite linear;
62 -o-animation: spin 2s infinite linear;
63 -webkit-animation: spin 2s infinite linear;
63 -webkit-animation: spin 2s infinite linear;
64 animation: spin 2s infinite linear;
64 animation: spin 2s infinite linear;
65 display: inline-block;
65 display: inline-block;
66 }
66 }
67 @-moz-keyframes spin {
67 @-moz-keyframes spin {
68 0% {
68 0% {
69 -moz-transform: rotate(0deg);
69 -moz-transform: rotate(0deg);
70 -o-transform: rotate(0deg);
70 -o-transform: rotate(0deg);
71 -webkit-transform: rotate(0deg);
71 -webkit-transform: rotate(0deg);
72 transform: rotate(0deg);
72 transform: rotate(0deg);
73 }
73 }
74
74
75 100% {
75 100% {
76 -moz-transform: rotate(359deg);
76 -moz-transform: rotate(359deg);
77 -o-transform: rotate(359deg);
77 -o-transform: rotate(359deg);
78 -webkit-transform: rotate(359deg);
78 -webkit-transform: rotate(359deg);
79 transform: rotate(359deg);
79 transform: rotate(359deg);
80 }
80 }
81 }
81 }
82 @-webkit-keyframes spin {
82 @-webkit-keyframes spin {
83 0% {
83 0% {
84 -moz-transform: rotate(0deg);
84 -moz-transform: rotate(0deg);
85 -o-transform: rotate(0deg);
85 -o-transform: rotate(0deg);
86 -webkit-transform: rotate(0deg);
86 -webkit-transform: rotate(0deg);
87 transform: rotate(0deg);
87 transform: rotate(0deg);
88 }
88 }
89
89
90 100% {
90 100% {
91 -moz-transform: rotate(359deg);
91 -moz-transform: rotate(359deg);
92 -o-transform: rotate(359deg);
92 -o-transform: rotate(359deg);
93 -webkit-transform: rotate(359deg);
93 -webkit-transform: rotate(359deg);
94 transform: rotate(359deg);
94 transform: rotate(359deg);
95 }
95 }
96 }
96 }
97 @-o-keyframes spin {
97 @-o-keyframes spin {
98 0% {
98 0% {
99 -moz-transform: rotate(0deg);
99 -moz-transform: rotate(0deg);
100 -o-transform: rotate(0deg);
100 -o-transform: rotate(0deg);
101 -webkit-transform: rotate(0deg);
101 -webkit-transform: rotate(0deg);
102 transform: rotate(0deg);
102 transform: rotate(0deg);
103 }
103 }
104
104
105 100% {
105 100% {
106 -moz-transform: rotate(359deg);
106 -moz-transform: rotate(359deg);
107 -o-transform: rotate(359deg);
107 -o-transform: rotate(359deg);
108 -webkit-transform: rotate(359deg);
108 -webkit-transform: rotate(359deg);
109 transform: rotate(359deg);
109 transform: rotate(359deg);
110 }
110 }
111 }
111 }
112 @-ms-keyframes spin {
112 @-ms-keyframes spin {
113 0% {
113 0% {
114 -moz-transform: rotate(0deg);
114 -moz-transform: rotate(0deg);
115 -o-transform: rotate(0deg);
115 -o-transform: rotate(0deg);
116 -webkit-transform: rotate(0deg);
116 -webkit-transform: rotate(0deg);
117 transform: rotate(0deg);
117 transform: rotate(0deg);
118 }
118 }
119
119
120 100% {
120 100% {
121 -moz-transform: rotate(359deg);
121 -moz-transform: rotate(359deg);
122 -o-transform: rotate(359deg);
122 -o-transform: rotate(359deg);
123 -webkit-transform: rotate(359deg);
123 -webkit-transform: rotate(359deg);
124 transform: rotate(359deg);
124 transform: rotate(359deg);
125 }
125 }
126 }
126 }
127 @keyframes spin {
127 @keyframes spin {
128 0% {
128 0% {
129 -moz-transform: rotate(0deg);
129 -moz-transform: rotate(0deg);
130 -o-transform: rotate(0deg);
130 -o-transform: rotate(0deg);
131 -webkit-transform: rotate(0deg);
131 -webkit-transform: rotate(0deg);
132 transform: rotate(0deg);
132 transform: rotate(0deg);
133 }
133 }
134
134
135 100% {
135 100% {
136 -moz-transform: rotate(359deg);
136 -moz-transform: rotate(359deg);
137 -o-transform: rotate(359deg);
137 -o-transform: rotate(359deg);
138 -webkit-transform: rotate(359deg);
138 -webkit-transform: rotate(359deg);
139 transform: rotate(359deg);
139 transform: rotate(359deg);
140 }
140 }
141 }
141 }
142
142
143
143
144
144
145 .icon-no-margin::before {
145 .icon-no-margin::before {
146 margin: 0;
146 margin: 0;
147
147
148 }
148 }
149 // -- ICON CLASSES -- //
149 // -- ICON CLASSES -- //
150 // sorter = lambda s: '\n'.join(sorted(s.splitlines()))
150 // sorter = lambda s: '\n'.join(sorted(s.splitlines()))
151
151
152 .icon-delete:before { content: '\e800'; } /* '' */
152 .icon-delete:before { content: '\e800'; } /* '' */
153 .icon-ok:before { content: '\e801'; } /* '' */
153 .icon-ok:before { content: '\e801'; } /* '' */
154 .icon-comment:before { content: '\e802'; } /* '' */
154 .icon-comment:before { content: '\e802'; } /* '' */
155 .icon-bookmark:before { content: '\e803'; } /* '' */
155 .icon-bookmark:before { content: '\e803'; } /* '' */
156 .icon-branch:before { content: '\e804'; } /* '' */
156 .icon-branch:before { content: '\e804'; } /* '' */
157 .icon-tag:before { content: '\e805'; } /* '' */
157 .icon-tag:before { content: '\e805'; } /* '' */
158 .icon-lock:before { content: '\e806'; } /* '' */
158 .icon-lock:before { content: '\e806'; } /* '' */
159 .icon-unlock:before { content: '\e807'; } /* '' */
159 .icon-unlock:before { content: '\e807'; } /* '' */
160 .icon-feed:before { content: '\e808'; } /* '' */
160 .icon-feed:before { content: '\e808'; } /* '' */
161 .icon-left:before { content: '\e809'; } /* '' */
161 .icon-left:before { content: '\e809'; } /* '' */
162 .icon-right:before { content: '\e80a'; } /* '' */
162 .icon-right:before { content: '\e80a'; } /* '' */
163 .icon-down:before { content: '\e80b'; } /* '' */
163 .icon-down:before { content: '\e80b'; } /* '' */
164 .icon-folder:before { content: '\e80c'; } /* '' */
164 .icon-folder:before { content: '\e80c'; } /* '' */
165 .icon-folder-open:before { content: '\e80d'; } /* '' */
165 .icon-folder-open:before { content: '\e80d'; } /* '' */
166 .icon-trash-empty:before { content: '\e80e'; } /* '' */
166 .icon-trash-empty:before { content: '\e80e'; } /* '' */
167 .icon-group:before { content: '\e80f'; } /* '' */
167 .icon-group:before { content: '\e80f'; } /* '' */
168 .icon-remove:before { content: '\e810'; } /* '' */
168 .icon-remove:before { content: '\e810'; } /* '' */
169 .icon-fork:before { content: '\e811'; } /* '' */
169 .icon-fork:before { content: '\e811'; } /* '' */
170 .icon-more:before { content: '\e812'; } /* '' */
170 .icon-more:before { content: '\e812'; } /* '' */
171 .icon-options:before { content: '\e812'; } /* '' */
171 .icon-options:before { content: '\e812'; } /* '' */
172 .icon-search:before { content: '\e813'; } /* '' */
172 .icon-search:before { content: '\e813'; } /* '' */
173 .icon-scissors:before { content: '\e814'; } /* '' */
173 .icon-scissors:before { content: '\e814'; } /* '' */
174 .icon-download:before { content: '\e815'; } /* '' */
174 .icon-download:before { content: '\e815'; } /* '' */
175 .icon-doc:before { content: '\e816'; } /* '' */
175 .icon-doc:before { content: '\e816'; } /* '' */
176 .icon-cog:before { content: '\e817'; } /* '' */
176 .icon-cog:before { content: '\e817'; } /* '' */
177 .icon-cog-alt:before { content: '\e818'; } /* '' */
177 .icon-cog-alt:before { content: '\e818'; } /* '' */
178 .icon-eye:before { content: '\e819'; } /* '' */
178 .icon-eye:before { content: '\e819'; } /* '' */
179 .icon-eye-off:before { content: '\e81a'; } /* '' */
179 .icon-eye-off:before { content: '\e81a'; } /* '' */
180 .icon-cancel-circled2:before { content: '\e81b'; } /* '' */
180 .icon-cancel-circled2:before { content: '\e81b'; } /* '' */
181 .icon-cancel-circled:before { content: '\e81c'; } /* '' */
181 .icon-cancel-circled:before { content: '\e81c'; } /* '' */
182 .icon-plus:before { content: '\e81d'; } /* '' */
182 .icon-plus:before { content: '\e81d'; } /* '' */
183 .icon-plus-circled:before { content: '\e81e'; } /* '' */
183 .icon-plus-circled:before { content: '\e81e'; } /* '' */
184 .icon-minus-circled:before { content: '\e81f'; } /* '' */
184 .icon-minus-circled:before { content: '\e81f'; } /* '' */
185 .icon-minus:before { content: '\e820'; } /* '' */
185 .icon-minus:before { content: '\e820'; } /* '' */
186 .icon-info-circled:before { content: '\e821'; } /* '' */
186 .icon-info-circled:before { content: '\e821'; } /* '' */
187 .icon-upload:before { content: '\e822'; } /* '' */
187 .icon-upload:before { content: '\e822'; } /* '' */
188 .icon-home:before { content: '\e823'; } /* '' */
188 .icon-home:before { content: '\e823'; } /* '' */
189 .icon-flag-filled:before { content: '\e824'; } /* '' */
189 .icon-flag-filled:before { content: '\e824'; } /* '' */
190 .icon-git:before { content: '\e82a'; } /* '' */
190 .icon-git:before { content: '\e82a'; } /* '' */
191 .icon-hg:before { content: '\e82d'; } /* '' */
191 .icon-hg:before { content: '\e82d'; } /* '' */
192 .icon-svn:before { content: '\e82e'; } /* '' */
192 .icon-svn:before { content: '\e82e'; } /* '' */
193 .icon-comment-add:before { content: '\e82f'; } /* '' */
193 .icon-comment-add:before { content: '\e82f'; } /* '' */
194 .icon-comment-toggle:before { content: '\e830'; } /* '' */
194 .icon-comment-toggle:before { content: '\e830'; } /* '' */
195 .icon-rhodecode:before { content: '\e831'; } /* '' */
195 .icon-rhodecode:before { content: '\e831'; } /* '' */
196 .icon-up:before { content: '\e832'; } /* '' */
196 .icon-up:before { content: '\e832'; } /* '' */
197 .icon-down:before { content: '\e832'; } /* '' */
197 .icon-merge:before { content: '\e833'; } /* '' */
198 .icon-merge:before { content: '\e833'; } /* '' */
198 .icon-spin-alt:before { content: '\e834'; } /* '' */
199 .icon-spin-alt:before { content: '\e834'; } /* '' */
199 .icon-spin:before { content: '\e838'; } /* '' */
200 .icon-spin:before { content: '\e838'; } /* '' */
200 .icon-docs:before { content: '\f0c5'; } /* '' */
201 .icon-docs:before { content: '\f0c5'; } /* '' */
201 .icon-menu:before { content: '\f0c9'; } /* '' */
202 .icon-menu:before { content: '\f0c9'; } /* '' */
202 .icon-sort:before { content: '\f0dc'; } /* '' */
203 .icon-sort:before { content: '\f0dc'; } /* '' */
203 .icon-paste:before { content: '\f0ea'; } /* '' */
204 .icon-paste:before { content: '\f0ea'; } /* '' */
204 .icon-doc-text:before { content: '\f0f6'; } /* '' */
205 .icon-doc-text:before { content: '\f0f6'; } /* '' */
205 .icon-plus-squared:before { content: '\f0fe'; } /* '' */
206 .icon-plus-squared:before { content: '\f0fe'; } /* '' */
206 .icon-angle-left:before { content: '\f104'; } /* '' */
207 .icon-angle-left:before { content: '\f104'; } /* '' */
207 .icon-angle-right:before { content: '\f105'; } /* '' */
208 .icon-angle-right:before { content: '\f105'; } /* '' */
208 .icon-angle-up:before { content: '\f106'; } /* '' */
209 .icon-angle-up:before { content: '\f106'; } /* '' */
209 .icon-angle-down:before { content: '\f107'; } /* '' */
210 .icon-angle-down:before { content: '\f107'; } /* '' */
210 .icon-circle-empty:before { content: '\f10c'; } /* '' */
211 .icon-circle-empty:before { content: '\f10c'; } /* '' */
211 .icon-circle:before { content: '\f111'; } /* '' */
212 .icon-circle:before { content: '\f111'; } /* '' */
212 .icon-folder-empty:before { content: '\f114'; } /* '' */
213 .icon-folder-empty:before { content: '\f114'; } /* '' */
213 .icon-folder-open-empty:before { content: '\f115'; } /* '' */
214 .icon-folder-open-empty:before { content: '\f115'; } /* '' */
214 .icon-code:before { content: '\f121'; } /* '' */
215 .icon-code:before { content: '\f121'; } /* '' */
215 .icon-info:before { content: '\f129'; } /* '' */
216 .icon-info:before { content: '\f129'; } /* '' */
216 .icon-minus-squared:before { content: '\f146'; } /* '' */
217 .icon-minus-squared:before { content: '\f146'; } /* '' */
217 .icon-minus-squared-alt:before { content: '\f147'; } /* '' */
218 .icon-minus-squared-alt:before { content: '\f147'; } /* '' */
218 .icon-doc-inv:before { content: '\f15b'; } /* '' */
219 .icon-doc-inv:before { content: '\f15b'; } /* '' */
219 .icon-doc-text-inv:before { content: '\f15c'; } /* '' */
220 .icon-doc-text-inv:before { content: '\f15c'; } /* '' */
220 .icon-plus-squared-alt:before { content: '\f196'; } /* '' */
221 .icon-plus-squared-alt:before { content: '\f196'; } /* '' */
221 .icon-file-code:before { content: '\f1c9'; } /* '' */
222 .icon-file-code:before { content: '\f1c9'; } /* '' */
222 .icon-history:before { content: '\f1da'; } /* '' */
223 .icon-history:before { content: '\f1da'; } /* '' */
223 .icon-circle-thin:before { content: '\f1db'; } /* '' */
224 .icon-circle-thin:before { content: '\f1db'; } /* '' */
224 .icon-sliders:before { content: '\f1de'; } /* '' */
225 .icon-sliders:before { content: '\f1de'; } /* '' */
225 .icon-trash:before { content: '\f1f8'; } /* '' */
226 .icon-trash:before { content: '\f1f8'; } /* '' */
226
227
227
228
228 // MERGED ICONS BASED ON CURRENT ONES
229 // MERGED ICONS BASED ON CURRENT ONES
229 .icon-repo-group:before { &:extend(.icon-folder-open:before); }
230 .icon-repo-group:before { &:extend(.icon-folder-open:before); }
230 .icon-repo-private:before { &:extend(.icon-lock:before); }
231 .icon-repo-private:before { &:extend(.icon-lock:before); }
231 .icon-repo-lock:before { &:extend(.icon-lock:before); }
232 .icon-repo-lock:before { &:extend(.icon-lock:before); }
232 .icon-unlock-alt:before { &:extend(.icon-unlock:before); }
233 .icon-unlock-alt:before { &:extend(.icon-unlock:before); }
233 .icon-repo-unlock:before { &:extend(.icon-unlock:before); }
234 .icon-repo-unlock:before { &:extend(.icon-unlock:before); }
234 .icon-repo-public:before { &:extend(.icon-unlock:before); }
235 .icon-repo-public:before { &:extend(.icon-unlock:before); }
235 .icon-rss-sign:before { &:extend(.icon-feed:before); }
236 .icon-rss-sign:before { &:extend(.icon-feed:before); }
236 .icon-code-fork:before { &:extend(.icon-fork:before); }
237 .icon-code-fork:before { &:extend(.icon-fork:before); }
237 .icon-arrow_up:before { &:extend(.icon-up:before); }
238 .icon-arrow_up:before { &:extend(.icon-up:before); }
239 .icon-arrow_down:before { &:extend(.icon-down:before); }
238 .icon-file:before { &:extend(.icon-file-code:before); }
240 .icon-file:before { &:extend(.icon-file-code:before); }
239 .icon-file-text:before { &:extend(.icon-file-code:before); }
241 .icon-file-text:before { &:extend(.icon-file-code:before); }
240 .icon-directory:before { &:extend(.icon-folder:before); }
242 .icon-directory:before { &:extend(.icon-folder:before); }
241 .icon-more-linked:before { &:extend(.icon-more:before); }
243 .icon-more-linked:before { &:extend(.icon-more:before); }
242 .icon-clipboard:before { &:extend(.icon-docs:before); }
244 .icon-clipboard:before { &:extend(.icon-docs:before); }
243 .icon-copy:before { &:extend(.icon-docs:before); }
245 .icon-copy:before { &:extend(.icon-docs:before); }
244 .icon-true:before { &:extend(.icon-ok:before); }
246 .icon-true:before { &:extend(.icon-ok:before); }
245 .icon-false:before { &:extend(.icon-delete:before); }
247 .icon-false:before { &:extend(.icon-delete:before); }
246 .icon-expand-linked:before { &:extend(.icon-down:before); }
248 .icon-expand-linked:before { &:extend(.icon-down:before); }
247 .icon-pr-merge-fail:before { &:extend(.icon-delete:before); }
249 .icon-pr-merge-fail:before { &:extend(.icon-delete:before); }
248 .icon-wide-mode:before { &:extend(.icon-sort:before); }
250 .icon-wide-mode:before { &:extend(.icon-sort:before); }
249 .icon-flag-filled-red:before { &:extend(.icon-flag-filled:before); }
251 .icon-flag-filled-red:before { &:extend(.icon-flag-filled:before); }
250 .icon-user-group-alt:before { &:extend(.icon-group:before); }
252 .icon-user-group-alt:before { &:extend(.icon-group:before); }
251
253
252 // TRANSFORM
254 // TRANSFORM
253 .icon-merge:before {transform: rotate(180deg);}
255 .icon-merge:before {transform: rotate(180deg);}
254 .icon-wide-mode:before {transform: rotate(90deg);}
256 .icon-wide-mode:before {transform: rotate(90deg);}
255 .icon-options:before {transform: rotate(90deg);}
257 .icon-options:before {transform: rotate(90deg);}
258 .icon-down:before {transform: rotate(180deg);}
259
256
260
257 // -- END ICON CLASSES -- //
261 // -- END ICON CLASSES -- //
258
262
259
263
260 //--- ICONS STYLING ------------------//
264 //--- ICONS STYLING ------------------//
261
265
262 .icon-git { color: @color4 !important; }
266 .icon-git { color: @color4 !important; }
263 .icon-hg { color: @color8 !important; }
267 .icon-hg { color: @color8 !important; }
264 .icon-svn { color: @color1 !important; }
268 .icon-svn { color: @color1 !important; }
265 .icon-git-inv { color: @color4 !important; }
269 .icon-git-inv { color: @color4 !important; }
266 .icon-hg-inv { color: @color8 !important; }
270 .icon-hg-inv { color: @color8 !important; }
267 .icon-svn-inv { color: @color1 !important; }
271 .icon-svn-inv { color: @color1 !important; }
268 .icon-repo-lock { color: #FF0000; }
272 .icon-repo-lock { color: #FF0000; }
269 .icon-repo-unlock { color: #FF0000; }
273 .icon-repo-unlock { color: #FF0000; }
270 .icon-false { color: @grey5 }
274 .icon-false { color: @grey5 }
271 .icon-expand-linked { cursor: pointer; color: @grey3; font-size: 14px }
275 .icon-expand-linked { cursor: pointer; color: @grey3; font-size: 14px }
272 .icon-more-linked { cursor: pointer; color: @grey3 }
276 .icon-more-linked { cursor: pointer; color: @grey3 }
273 .icon-flag-filled-red { color: @color5 !important; }
277 .icon-flag-filled-red { color: @color5 !important; }
274 .icon-filled-red { color: @color5 !important; }
278 .icon-filled-red { color: @color5 !important; }
275
279
276 .repo-switcher-dropdown .select2-result-label {
280 .repo-switcher-dropdown .select2-result-label {
277 .icon-git:before {
281 .icon-git:before {
278 &:extend(.icon-git-transparent:before);
282 &:extend(.icon-git-transparent:before);
279 }
283 }
280 .icon-hg:before {
284 .icon-hg:before {
281 &:extend(.icon-hg-transparent:before);
285 &:extend(.icon-hg-transparent:before);
282 color: @alert4;
286 color: @alert4;
283 }
287 }
284 .icon-svn:before {
288 .icon-svn:before {
285 &:extend(.icon-svn-transparent:before);
289 &:extend(.icon-svn-transparent:before);
286 }
290 }
287 }
291 }
288
292
289 .icon-user-group:before {
293 .icon-user-group:before {
290 &:extend(.icon-group:before);
294 &:extend(.icon-group:before);
291 margin: 0;
295 margin: 0;
292 font-size: 16px;
296 font-size: 16px;
293 }
297 }
@@ -1,716 +1,724 b''
1 // # Copyright (C) 2010-2020 RhodeCode GmbH
1 // # Copyright (C) 2010-2020 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
5 // # (only), as published by the Free Software Foundation.
5 // # (only), as published by the Free Software Foundation.
6 // #
6 // #
7 // # This program is distributed in the hope that it will be useful,
7 // # This program is distributed in the hope that it will be useful,
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // # GNU General Public License for more details.
10 // # GNU General Public License for more details.
11 // #
11 // #
12 // # You should have received a copy of the GNU Affero General Public License
12 // # You should have received a copy of the GNU Affero General Public License
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 // #
14 // #
15 // # This program is dual-licensed. If you wish to learn more about the
15 // # This program is dual-licensed. If you wish to learn more about the
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19 /**
19 /**
20 RhodeCode JS Files
20 RhodeCode JS Files
21 **/
21 **/
22
22
23 if (typeof console == "undefined" || typeof console.log == "undefined"){
23 if (typeof console == "undefined" || typeof console.log == "undefined"){
24 console = { log: function() {} }
24 console = { log: function() {} }
25 }
25 }
26
26
27 // TODO: move the following function to submodules
27 // TODO: move the following function to submodules
28
28
29 /**
29 /**
30 * show more
30 * show more
31 */
31 */
32 var show_more_event = function(){
32 var show_more_event = function(){
33 $('table .show_more').click(function(e) {
33 $('table .show_more').click(function(e) {
34 var cid = e.target.id.substring(1);
34 var cid = e.target.id.substring(1);
35 var button = $(this);
35 var button = $(this);
36 if (button.hasClass('open')) {
36 if (button.hasClass('open')) {
37 $('#'+cid).hide();
37 $('#'+cid).hide();
38 button.removeClass('open');
38 button.removeClass('open');
39 } else {
39 } else {
40 $('#'+cid).show();
40 $('#'+cid).show();
41 button.addClass('open one');
41 button.addClass('open one');
42 }
42 }
43 });
43 });
44 };
44 };
45
45
46 var compare_radio_buttons = function(repo_name, compare_ref_type){
46 var compare_radio_buttons = function(repo_name, compare_ref_type){
47 $('#compare_action').on('click', function(e){
47 $('#compare_action').on('click', function(e){
48 e.preventDefault();
48 e.preventDefault();
49
49
50 var source = $('input[name=compare_source]:checked').val();
50 var source = $('input[name=compare_source]:checked').val();
51 var target = $('input[name=compare_target]:checked').val();
51 var target = $('input[name=compare_target]:checked').val();
52 if(source && target){
52 if(source && target){
53 var url_data = {
53 var url_data = {
54 repo_name: repo_name,
54 repo_name: repo_name,
55 source_ref: source,
55 source_ref: source,
56 source_ref_type: compare_ref_type,
56 source_ref_type: compare_ref_type,
57 target_ref: target,
57 target_ref: target,
58 target_ref_type: compare_ref_type,
58 target_ref_type: compare_ref_type,
59 merge: 1
59 merge: 1
60 };
60 };
61 window.location = pyroutes.url('repo_compare', url_data);
61 window.location = pyroutes.url('repo_compare', url_data);
62 }
62 }
63 });
63 });
64 $('.compare-radio-button').on('click', function(e){
64 $('.compare-radio-button').on('click', function(e){
65 var source = $('input[name=compare_source]:checked').val();
65 var source = $('input[name=compare_source]:checked').val();
66 var target = $('input[name=compare_target]:checked').val();
66 var target = $('input[name=compare_target]:checked').val();
67 if(source && target){
67 if(source && target){
68 $('#compare_action').removeAttr("disabled");
68 $('#compare_action').removeAttr("disabled");
69 $('#compare_action').removeClass("disabled");
69 $('#compare_action').removeClass("disabled");
70 }
70 }
71 })
71 })
72 };
72 };
73
73
74 var showRepoSize = function(target, repo_name, commit_id, callback) {
74 var showRepoSize = function(target, repo_name, commit_id, callback) {
75 var container = $('#' + target);
75 var container = $('#' + target);
76 var url = pyroutes.url('repo_stats',
76 var url = pyroutes.url('repo_stats',
77 {"repo_name": repo_name, "commit_id": commit_id});
77 {"repo_name": repo_name, "commit_id": commit_id});
78
78
79 container.show();
79 container.show();
80 if (!container.hasClass('loaded')) {
80 if (!container.hasClass('loaded')) {
81 $.ajax({url: url})
81 $.ajax({url: url})
82 .complete(function (data) {
82 .complete(function (data) {
83 var responseJSON = data.responseJSON;
83 var responseJSON = data.responseJSON;
84 container.addClass('loaded');
84 container.addClass('loaded');
85 container.html(responseJSON.size);
85 container.html(responseJSON.size);
86 callback(responseJSON.code_stats)
86 callback(responseJSON.code_stats)
87 })
87 })
88 .fail(function (data) {
88 .fail(function (data) {
89 console.log('failed to load repo stats');
89 console.log('failed to load repo stats');
90 });
90 });
91 }
91 }
92
92
93 };
93 };
94
94
95 var showRepoStats = function(target, data){
95 var showRepoStats = function(target, data){
96 var container = $('#' + target);
96 var container = $('#' + target);
97
97
98 if (container.hasClass('loaded')) {
98 if (container.hasClass('loaded')) {
99 return
99 return
100 }
100 }
101
101
102 var total = 0;
102 var total = 0;
103 var no_data = true;
103 var no_data = true;
104 var tbl = document.createElement('table');
104 var tbl = document.createElement('table');
105 tbl.setAttribute('class', 'trending_language_tbl rctable');
105 tbl.setAttribute('class', 'trending_language_tbl rctable');
106
106
107 $.each(data, function(key, val){
107 $.each(data, function(key, val){
108 total += val.count;
108 total += val.count;
109 });
109 });
110
110
111 var sortedStats = [];
111 var sortedStats = [];
112 for (var obj in data){
112 for (var obj in data){
113 sortedStats.push([obj, data[obj]])
113 sortedStats.push([obj, data[obj]])
114 }
114 }
115 var sortedData = sortedStats.sort(function (a, b) {
115 var sortedData = sortedStats.sort(function (a, b) {
116 return b[1].count - a[1].count
116 return b[1].count - a[1].count
117 });
117 });
118 var cnt = 0;
118 var cnt = 0;
119 $.each(sortedData, function(idx, val){
119 $.each(sortedData, function(idx, val){
120 cnt += 1;
120 cnt += 1;
121 no_data = false;
121 no_data = false;
122
122
123 var tr = document.createElement('tr');
123 var tr = document.createElement('tr');
124
124
125 var key = val[0];
125 var key = val[0];
126 var obj = {"desc": val[1].desc, "count": val[1].count};
126 var obj = {"desc": val[1].desc, "count": val[1].count};
127
127
128 // meta language names
128 // meta language names
129 var td1 = document.createElement('td');
129 var td1 = document.createElement('td');
130 var trending_language_label = document.createElement('div');
130 var trending_language_label = document.createElement('div');
131 trending_language_label.innerHTML = obj.desc;
131 trending_language_label.innerHTML = obj.desc;
132 td1.appendChild(trending_language_label);
132 td1.appendChild(trending_language_label);
133
133
134 // extensions
134 // extensions
135 var td2 = document.createElement('td');
135 var td2 = document.createElement('td');
136 var extension = document.createElement('div');
136 var extension = document.createElement('div');
137 extension.innerHTML = ".{0}".format(key)
137 extension.innerHTML = ".{0}".format(key)
138 td2.appendChild(extension);
138 td2.appendChild(extension);
139
139
140 // number of files
140 // number of files
141 var td3 = document.createElement('td');
141 var td3 = document.createElement('td');
142 var file_count = document.createElement('div');
142 var file_count = document.createElement('div');
143 var percentage_num = Math.round((obj.count / total * 100), 2);
143 var percentage_num = Math.round((obj.count / total * 100), 2);
144 var label = _ngettext('file', 'files', obj.count);
144 var label = _ngettext('file', 'files', obj.count);
145 file_count.innerHTML = "{0} {1} ({2}%)".format(obj.count, label, percentage_num) ;
145 file_count.innerHTML = "{0} {1} ({2}%)".format(obj.count, label, percentage_num) ;
146 td3.appendChild(file_count);
146 td3.appendChild(file_count);
147
147
148 // percentage
148 // percentage
149 var td4 = document.createElement('td');
149 var td4 = document.createElement('td');
150 td4.setAttribute("class", 'trending_language');
150 td4.setAttribute("class", 'trending_language');
151
151
152 var percentage = document.createElement('div');
152 var percentage = document.createElement('div');
153 percentage.setAttribute('class', 'lang-bar');
153 percentage.setAttribute('class', 'lang-bar');
154 percentage.innerHTML = "&nbsp;";
154 percentage.innerHTML = "&nbsp;";
155 percentage.style.width = percentage_num + '%';
155 percentage.style.width = percentage_num + '%';
156 td4.appendChild(percentage);
156 td4.appendChild(percentage);
157
157
158 tr.appendChild(td1);
158 tr.appendChild(td1);
159 tr.appendChild(td2);
159 tr.appendChild(td2);
160 tr.appendChild(td3);
160 tr.appendChild(td3);
161 tr.appendChild(td4);
161 tr.appendChild(td4);
162 tbl.appendChild(tr);
162 tbl.appendChild(tr);
163
163
164 });
164 });
165
165
166 $(container).html(tbl);
166 $(container).html(tbl);
167 $(container).addClass('loaded');
167 $(container).addClass('loaded');
168
168
169 $('#code_stats_show_more').on('click', function (e) {
169 $('#code_stats_show_more').on('click', function (e) {
170 e.preventDefault();
170 e.preventDefault();
171 $('.stats_hidden').each(function (idx) {
171 $('.stats_hidden').each(function (idx) {
172 $(this).css("display", "");
172 $(this).css("display", "");
173 });
173 });
174 $('#code_stats_show_more').hide();
174 $('#code_stats_show_more').hide();
175 });
175 });
176
176
177 };
177 };
178
178
179 // returns a node from given html;
179 // returns a node from given html;
180 var fromHTML = function(html){
180 var fromHTML = function(html){
181 var _html = document.createElement('element');
181 var _html = document.createElement('element');
182 _html.innerHTML = html;
182 _html.innerHTML = html;
183 return _html;
183 return _html;
184 };
184 };
185
185
186 // Toggle Collapsable Content
186 // Toggle Collapsable Content
187 function collapsableContent() {
187 function collapsableContent() {
188
188
189 $('.collapsable-content').not('.no-hide').hide();
189 $('.collapsable-content').not('.no-hide').hide();
190
190
191 $('.btn-collapse').unbind(); //in case we've been here before
191 $('.btn-collapse').unbind(); //in case we've been here before
192 $('.btn-collapse').click(function() {
192 $('.btn-collapse').click(function() {
193 var button = $(this);
193 var button = $(this);
194 var togglename = $(this).data("toggle");
194 var togglename = $(this).data("toggle");
195 $('.collapsable-content[data-toggle='+togglename+']').toggle();
195 $('.collapsable-content[data-toggle='+togglename+']').toggle();
196 if ($(this).html()=="Show Less")
196 if ($(this).html()=="Show Less")
197 $(this).html("Show More");
197 $(this).html("Show More");
198 else
198 else
199 $(this).html("Show Less");
199 $(this).html("Show Less");
200 });
200 });
201 };
201 };
202
202
203 var timeagoActivate = function() {
203 var timeagoActivate = function() {
204 $("time.timeago").timeago();
204 $("time.timeago").timeago();
205 };
205 };
206
206
207
207
208 var clipboardActivate = function() {
208 var clipboardActivate = function() {
209 /*
209 /*
210 *
210 *
211 * <i class="tooltip icon-plus clipboard-action" data-clipboard-text="${commit.raw_id}" title="${_('Copy the full commit id')}"></i>
211 * <i class="tooltip icon-plus clipboard-action" data-clipboard-text="${commit.raw_id}" title="${_('Copy the full commit id')}"></i>
212 * */
212 * */
213 var clipboard = new ClipboardJS('.clipboard-action');
213 var clipboard = new ClipboardJS('.clipboard-action');
214
214
215 clipboard.on('success', function(e) {
215 clipboard.on('success', function(e) {
216 var callback = function () {
216 var callback = function () {
217 $(e.trigger).animate({'opacity': 1.00}, 200)
217 $(e.trigger).animate({'opacity': 1.00}, 200)
218 };
218 };
219 $(e.trigger).animate({'opacity': 0.15}, 200, callback);
219 $(e.trigger).animate({'opacity': 0.15}, 200, callback);
220 e.clearSelection();
220 e.clearSelection();
221 });
221 });
222 };
222 };
223
223
224 var tooltipActivate = function () {
224 var tooltipActivate = function () {
225 var delay = 50;
225 var delay = 50;
226 var animation = 'fade';
226 var animation = 'fade';
227 var theme = 'tooltipster-shadow';
227 var theme = 'tooltipster-shadow';
228 var debug = false;
228 var debug = false;
229
229
230 $('.tooltip').tooltipster({
230 $('.tooltip').tooltipster({
231 debug: debug,
231 debug: debug,
232 theme: theme,
232 theme: theme,
233 animation: animation,
233 animation: animation,
234 delay: delay,
234 delay: delay,
235 contentCloning: true,
235 contentCloning: true,
236 contentAsHTML: true,
236 contentAsHTML: true,
237
237
238 functionBefore: function (instance, helper) {
238 functionBefore: function (instance, helper) {
239 var $origin = $(helper.origin);
239 var $origin = $(helper.origin);
240 var data = '<div style="white-space: pre-wrap">{0}</div>'.format(instance.content());
240 var data = '<div style="white-space: pre-wrap">{0}</div>'.format(instance.content());
241 instance.content(data);
241 instance.content(data);
242 }
242 }
243 });
243 });
244 var hovercardCache = {};
244 var hovercardCache = {};
245
245
246 var loadHoverCard = function (url, altHovercard, callback) {
246 var loadHoverCard = function (url, altHovercard, callback) {
247 var id = url;
247 var id = url;
248
248
249 if (hovercardCache[id] !== undefined) {
249 if (hovercardCache[id] !== undefined) {
250 callback(hovercardCache[id]);
250 callback(hovercardCache[id]);
251 return true;
251 return true;
252 }
252 }
253
253
254 hovercardCache[id] = undefined;
254 hovercardCache[id] = undefined;
255 $.get(url, function (data) {
255 $.get(url, function (data) {
256 hovercardCache[id] = data;
256 hovercardCache[id] = data;
257 callback(hovercardCache[id]);
257 callback(hovercardCache[id]);
258 return true;
258 return true;
259 }).fail(function (data, textStatus, errorThrown) {
259 }).fail(function (data, textStatus, errorThrown) {
260
260
261 if (parseInt(data.status) === 404) {
261 if (parseInt(data.status) === 404) {
262 var msg = "<p>{0}</p>".format(altHovercard || "No Data exists for this hovercard");
262 var msg = "<p>{0}</p>".format(altHovercard || "No Data exists for this hovercard");
263 } else {
263 } else {
264 var msg = "<p class='error-message'>Error while fetching hovercard.\nError code {0} ({1}).</p>".format(data.status,data.statusText);
264 var msg = "<p class='error-message'>Error while fetching hovercard.\nError code {0} ({1}).</p>".format(data.status,data.statusText);
265 }
265 }
266 callback(msg);
266 callback(msg);
267 return false
267 return false
268 });
268 });
269 };
269 };
270
270
271 $('.tooltip-hovercard').tooltipster({
271 $('.tooltip-hovercard').tooltipster({
272 debug: debug,
272 debug: debug,
273 theme: theme,
273 theme: theme,
274 animation: animation,
274 animation: animation,
275 delay: delay,
275 delay: delay,
276 interactive: true,
276 interactive: true,
277 contentCloning: true,
277 contentCloning: true,
278
278
279 trigger: 'custom',
279 trigger: 'custom',
280 triggerOpen: {
280 triggerOpen: {
281 mouseenter: true,
281 mouseenter: true,
282 },
282 },
283 triggerClose: {
283 triggerClose: {
284 mouseleave: true,
284 mouseleave: true,
285 originClick: true,
285 originClick: true,
286 touchleave: true
286 touchleave: true
287 },
287 },
288 content: _gettext('Loading...'),
288 content: _gettext('Loading...'),
289 contentAsHTML: true,
289 contentAsHTML: true,
290 updateAnimation: null,
290 updateAnimation: null,
291
291
292 functionBefore: function (instance, helper) {
292 functionBefore: function (instance, helper) {
293
293
294 var $origin = $(helper.origin);
294 var $origin = $(helper.origin);
295
295
296 // we set a variable so the data is only loaded once via Ajax, not every time the tooltip opens
296 // we set a variable so the data is only loaded once via Ajax, not every time the tooltip opens
297 if ($origin.data('loaded') !== true) {
297 if ($origin.data('loaded') !== true) {
298 var hovercardUrl = $origin.data('hovercardUrl');
298 var hovercardUrl = $origin.data('hovercardUrl');
299 var altHovercard = $origin.data('hovercardAlt');
299 var altHovercard = $origin.data('hovercardAlt');
300
300
301 if (hovercardUrl !== undefined && hovercardUrl !== "") {
301 if (hovercardUrl !== undefined && hovercardUrl !== "") {
302 var urlLoad = true;
302 var urlLoad = true;
303 if (hovercardUrl.substr(0, 12) === 'pyroutes.url') {
303 if (hovercardUrl.substr(0, 12) === 'pyroutes.url') {
304 hovercardUrl = eval(hovercardUrl)
304 hovercardUrl = eval(hovercardUrl)
305 } else if (hovercardUrl.substr(0, 11) === 'javascript:') {
305 } else if (hovercardUrl.substr(0, 11) === 'javascript:') {
306 var jsFunc = hovercardUrl.substr(11);
306 var jsFunc = hovercardUrl.substr(11);
307 urlLoad = false;
307 urlLoad = false;
308 loaded = true;
308 loaded = true;
309 instance.content(eval(jsFunc))
309 instance.content(eval(jsFunc))
310 }
310 }
311
311
312 if (urlLoad) {
312 if (urlLoad) {
313 var loaded = loadHoverCard(hovercardUrl, altHovercard, function (data) {
313 var loaded = loadHoverCard(hovercardUrl, altHovercard, function (data) {
314 instance.content(data);
314 instance.content(data);
315 })
315 })
316 }
316 }
317
317
318 } else {
318 } else {
319 if ($origin.data('hovercardAltHtml')) {
319 if ($origin.data('hovercardAltHtml')) {
320 var data = atob($origin.data('hovercardAltHtml'));
320 var data = atob($origin.data('hovercardAltHtml'));
321 } else {
321 } else {
322 var data = '<div style="white-space: pre-wrap">{0}</div>'.format(altHovercard)
322 var data = '<div style="white-space: pre-wrap">{0}</div>'.format(altHovercard)
323 }
323 }
324 var loaded = true;
324 var loaded = true;
325 instance.content(data);
325 instance.content(data);
326 }
326 }
327
327
328 // to remember that the data has been loaded
328 // to remember that the data has been loaded
329 $origin.data('loaded', loaded);
329 $origin.data('loaded', loaded);
330 }
330 }
331 }
331 }
332 })
332 })
333 };
333 };
334
334
335 // Formatting values in a Select2 dropdown of commit references
335 // Formatting values in a Select2 dropdown of commit references
336 var formatSelect2SelectionRefs = function(commit_ref){
336 var formatSelect2SelectionRefs = function(commit_ref){
337 var tmpl = '';
337 var tmpl = '';
338 if (!commit_ref.text || commit_ref.type === 'sha'){
338 if (!commit_ref.text || commit_ref.type === 'sha'){
339 return commit_ref.text;
339 return commit_ref.text;
340 }
340 }
341 if (commit_ref.type === 'branch'){
341 if (commit_ref.type === 'branch'){
342 tmpl = tmpl.concat('<i class="icon-branch"></i> ');
342 tmpl = tmpl.concat('<i class="icon-branch"></i> ');
343 } else if (commit_ref.type === 'tag'){
343 } else if (commit_ref.type === 'tag'){
344 tmpl = tmpl.concat('<i class="icon-tag"></i> ');
344 tmpl = tmpl.concat('<i class="icon-tag"></i> ');
345 } else if (commit_ref.type === 'book'){
345 } else if (commit_ref.type === 'book'){
346 tmpl = tmpl.concat('<i class="icon-bookmark"></i> ');
346 tmpl = tmpl.concat('<i class="icon-bookmark"></i> ');
347 }
347 }
348 return tmpl.concat(escapeHtml(commit_ref.text));
348 return tmpl.concat(escapeHtml(commit_ref.text));
349 };
349 };
350
350
351 // takes a given html element and scrolls it down offset pixels
351 // takes a given html element and scrolls it down offset pixels
352 function offsetScroll(element, offset) {
352 function offsetScroll(element, offset) {
353 setTimeout(function() {
353 setTimeout(function() {
354 var location = element.offset().top;
354 var location = element.offset().top;
355 // some browsers use body, some use html
355 // some browsers use body, some use html
356 $('html, body').animate({ scrollTop: (location - offset) });
356 $('html, body').animate({ scrollTop: (location - offset) });
357 }, 100);
357 }, 100);
358 }
358 }
359
359
360 // scroll an element `percent`% from the top of page in `time` ms
360 // scroll an element `percent`% from the top of page in `time` ms
361 function scrollToElement(element, percent, time) {
361 function scrollToElement(element, percent, time) {
362 percent = (percent === undefined ? 25 : percent);
362 percent = (percent === undefined ? 25 : percent);
363 time = (time === undefined ? 100 : time);
363 time = (time === undefined ? 100 : time);
364
364
365 var $element = $(element);
365 var $element = $(element);
366 if ($element.length == 0) {
366 if ($element.length == 0) {
367 throw('Cannot scroll to {0}'.format(element))
367 throw('Cannot scroll to {0}'.format(element))
368 }
368 }
369 var elOffset = $element.offset().top;
369 var elOffset = $element.offset().top;
370 var elHeight = $element.height();
370 var elHeight = $element.height();
371 var windowHeight = $(window).height();
371 var windowHeight = $(window).height();
372 var offset = elOffset;
372 var offset = elOffset;
373 if (elHeight < windowHeight) {
373 if (elHeight < windowHeight) {
374 offset = elOffset - ((windowHeight / (100 / percent)) - (elHeight / 2));
374 offset = elOffset - ((windowHeight / (100 / percent)) - (elHeight / 2));
375 }
375 }
376 setTimeout(function() {
376 setTimeout(function() {
377 $('html, body').animate({ scrollTop: offset});
377 $('html, body').animate({ scrollTop: offset});
378 }, time);
378 }, time);
379 }
379 }
380
380
381 /**
381 /**
382 * global hooks after DOM is loaded
382 * global hooks after DOM is loaded
383 */
383 */
384 $(document).ready(function() {
384 $(document).ready(function() {
385 firefoxAnchorFix();
385 firefoxAnchorFix();
386
386
387 $('.navigation a.menulink').on('click', function(e){
387 $('.navigation a.menulink').on('click', function(e){
388 var menuitem = $(this).parent('li');
388 var menuitem = $(this).parent('li');
389 if (menuitem.hasClass('open')) {
389 if (menuitem.hasClass('open')) {
390 menuitem.removeClass('open');
390 menuitem.removeClass('open');
391 } else {
391 } else {
392 menuitem.addClass('open');
392 menuitem.addClass('open');
393 $(document).on('click', function(event) {
393 $(document).on('click', function(event) {
394 if (!$(event.target).closest(menuitem).length) {
394 if (!$(event.target).closest(menuitem).length) {
395 menuitem.removeClass('open');
395 menuitem.removeClass('open');
396 }
396 }
397 });
397 });
398 }
398 }
399 });
399 });
400
400
401 $('body').on('click', '.cb-lineno a', function(event) {
401 $('body').on('click', '.cb-lineno a', function(event) {
402 function sortNumber(a,b) {
402 function sortNumber(a,b) {
403 return a - b;
403 return a - b;
404 }
404 }
405
405
406 var lineNo = $(this).data('lineNo');
406 var lineNo = $(this).data('lineNo');
407 var lineName = $(this).attr('name');
407 var lineName = $(this).attr('name');
408
408
409 if (lineNo) {
409 if (lineNo) {
410 var prevLine = $('.cb-line-selected a').data('lineNo');
410 var prevLine = $('.cb-line-selected a').data('lineNo');
411
411
412 // on shift, we do a range selection, if we got previous line
412 // on shift, we do a range selection, if we got previous line
413 if (event.shiftKey && prevLine !== undefined) {
413 if (event.shiftKey && prevLine !== undefined) {
414 var prevLine = parseInt(prevLine);
414 var prevLine = parseInt(prevLine);
415 var nextLine = parseInt(lineNo);
415 var nextLine = parseInt(lineNo);
416 var pos = [prevLine, nextLine].sort(sortNumber);
416 var pos = [prevLine, nextLine].sort(sortNumber);
417 var anchor = '#L{0}-{1}'.format(pos[0], pos[1]);
417 var anchor = '#L{0}-{1}'.format(pos[0], pos[1]);
418
418
419 // single click
419 // single click
420 } else {
420 } else {
421 var nextLine = parseInt(lineNo);
421 var nextLine = parseInt(lineNo);
422 var pos = [nextLine, nextLine];
422 var pos = [nextLine, nextLine];
423 var anchor = '#L{0}'.format(pos[0]);
423 var anchor = '#L{0}'.format(pos[0]);
424
424
425 }
425 }
426 // highlight
426 // highlight
427 var range = [];
427 var range = [];
428 for (var i = pos[0]; i <= pos[1]; i++) {
428 for (var i = pos[0]; i <= pos[1]; i++) {
429 range.push(i);
429 range.push(i);
430 }
430 }
431 // clear old selected lines
431 // clear old selected lines
432 $('.cb-line-selected').removeClass('cb-line-selected');
432 $('.cb-line-selected').removeClass('cb-line-selected');
433
433
434 $.each(range, function (i, lineNo) {
434 $.each(range, function (i, lineNo) {
435 var line_td = $('td.cb-lineno#L' + lineNo);
435 var line_td = $('td.cb-lineno#L' + lineNo);
436
436
437 if (line_td.length) {
437 if (line_td.length) {
438 line_td.addClass('cb-line-selected'); // line number td
438 line_td.addClass('cb-line-selected'); // line number td
439 line_td.prev().addClass('cb-line-selected'); // line data
439 line_td.prev().addClass('cb-line-selected'); // line data
440 line_td.next().addClass('cb-line-selected'); // line content
440 line_td.next().addClass('cb-line-selected'); // line content
441 }
441 }
442 });
442 });
443
443
444 } else if (lineName !== undefined) { // lineName only occurs in diffs
444 } else if (lineName !== undefined) { // lineName only occurs in diffs
445 // clear old selected lines
445 // clear old selected lines
446 $('td.cb-line-selected').removeClass('cb-line-selected');
446 $('td.cb-line-selected').removeClass('cb-line-selected');
447 var anchor = '#{0}'.format(lineName);
447 var anchor = '#{0}'.format(lineName);
448 var diffmode = templateContext.session_attrs.diffmode || "sideside";
448 var diffmode = templateContext.session_attrs.diffmode || "sideside";
449
449
450 if (diffmode === "unified") {
450 if (diffmode === "unified") {
451 $(this).closest('tr').find('td').addClass('cb-line-selected');
451 $(this).closest('tr').find('td').addClass('cb-line-selected');
452 } else {
452 } else {
453 var activeTd = $(this).closest('td');
453 var activeTd = $(this).closest('td');
454 activeTd.addClass('cb-line-selected');
454 activeTd.addClass('cb-line-selected');
455 activeTd.next('td').addClass('cb-line-selected');
455 activeTd.next('td').addClass('cb-line-selected');
456 }
456 }
457
457
458 }
458 }
459
459
460 // Replace URL without jumping to it if browser supports.
460 // Replace URL without jumping to it if browser supports.
461 // Default otherwise
461 // Default otherwise
462 if (history.pushState && anchor !== undefined) {
462 if (history.pushState && anchor !== undefined) {
463 var new_location = location.href.rstrip('#');
463 var new_location = location.href.rstrip('#');
464 if (location.hash) {
464 if (location.hash) {
465 // location without hash
465 // location without hash
466 new_location = new_location.replace(location.hash, "");
466 new_location = new_location.replace(location.hash, "");
467 }
467 }
468
468
469 // Make new anchor url
469 // Make new anchor url
470 new_location = new_location + anchor;
470 new_location = new_location + anchor;
471 history.pushState(true, document.title, new_location);
471 history.pushState(true, document.title, new_location);
472
472
473 return false;
473 return false;
474 }
474 }
475
475
476 });
476 });
477
477
478 $('.collapse_file').on('click', function(e) {
478 $('.collapse_file').on('click', function(e) {
479 e.stopPropagation();
479 e.stopPropagation();
480 if ($(e.target).is('a')) { return; }
480 if ($(e.target).is('a')) { return; }
481 var node = $(e.delegateTarget).first();
481 var node = $(e.delegateTarget).first();
482 var icon = $($(node.children().first()).children().first());
482 var icon = $($(node.children().first()).children().first());
483 var id = node.attr('fid');
483 var id = node.attr('fid');
484 var target = $('#'+id);
484 var target = $('#'+id);
485 var tr = $('#tr_'+id);
485 var tr = $('#tr_'+id);
486 var diff = $('#diff_'+id);
486 var diff = $('#diff_'+id);
487 if(node.hasClass('expand_file')){
487 if(node.hasClass('expand_file')){
488 node.removeClass('expand_file');
488 node.removeClass('expand_file');
489 icon.removeClass('expand_file_icon');
489 icon.removeClass('expand_file_icon');
490 node.addClass('collapse_file');
490 node.addClass('collapse_file');
491 icon.addClass('collapse_file_icon');
491 icon.addClass('collapse_file_icon');
492 diff.show();
492 diff.show();
493 tr.show();
493 tr.show();
494 target.show();
494 target.show();
495 } else {
495 } else {
496 node.removeClass('collapse_file');
496 node.removeClass('collapse_file');
497 icon.removeClass('collapse_file_icon');
497 icon.removeClass('collapse_file_icon');
498 node.addClass('expand_file');
498 node.addClass('expand_file');
499 icon.addClass('expand_file_icon');
499 icon.addClass('expand_file_icon');
500 diff.hide();
500 diff.hide();
501 tr.hide();
501 tr.hide();
502 target.hide();
502 target.hide();
503 }
503 }
504 });
504 });
505
505
506 $('#expand_all_files').click(function() {
506 $('#expand_all_files').click(function() {
507 $('.expand_file').each(function() {
507 $('.expand_file').each(function() {
508 var node = $(this);
508 var node = $(this);
509 var icon = $($(node.children().first()).children().first());
509 var icon = $($(node.children().first()).children().first());
510 var id = $(this).attr('fid');
510 var id = $(this).attr('fid');
511 var target = $('#'+id);
511 var target = $('#'+id);
512 var tr = $('#tr_'+id);
512 var tr = $('#tr_'+id);
513 var diff = $('#diff_'+id);
513 var diff = $('#diff_'+id);
514 node.removeClass('expand_file');
514 node.removeClass('expand_file');
515 icon.removeClass('expand_file_icon');
515 icon.removeClass('expand_file_icon');
516 node.addClass('collapse_file');
516 node.addClass('collapse_file');
517 icon.addClass('collapse_file_icon');
517 icon.addClass('collapse_file_icon');
518 diff.show();
518 diff.show();
519 tr.show();
519 tr.show();
520 target.show();
520 target.show();
521 });
521 });
522 });
522 });
523
523
524 $('#collapse_all_files').click(function() {
524 $('#collapse_all_files').click(function() {
525 $('.collapse_file').each(function() {
525 $('.collapse_file').each(function() {
526 var node = $(this);
526 var node = $(this);
527 var icon = $($(node.children().first()).children().first());
527 var icon = $($(node.children().first()).children().first());
528 var id = $(this).attr('fid');
528 var id = $(this).attr('fid');
529 var target = $('#'+id);
529 var target = $('#'+id);
530 var tr = $('#tr_'+id);
530 var tr = $('#tr_'+id);
531 var diff = $('#diff_'+id);
531 var diff = $('#diff_'+id);
532 node.removeClass('collapse_file');
532 node.removeClass('collapse_file');
533 icon.removeClass('collapse_file_icon');
533 icon.removeClass('collapse_file_icon');
534 node.addClass('expand_file');
534 node.addClass('expand_file');
535 icon.addClass('expand_file_icon');
535 icon.addClass('expand_file_icon');
536 diff.hide();
536 diff.hide();
537 tr.hide();
537 tr.hide();
538 target.hide();
538 target.hide();
539 });
539 });
540 });
540 });
541
541
542 // Mouse over behavior for comments and line selection
542 // Mouse over behavior for comments and line selection
543
543
544 // Select the line that comes from the url anchor
544 // Select the line that comes from the url anchor
545 // At the time of development, Chrome didn't seem to support jquery's :target
545 // At the time of development, Chrome didn't seem to support jquery's :target
546 // element, so I had to scroll manually
546 // element, so I had to scroll manually
547
547
548 if (location.hash) {
548 if (location.hash) {
549 var result = splitDelimitedHash(location.hash);
549 var result = splitDelimitedHash(location.hash);
550
550
551 var loc = result.loc;
551 var loc = result.loc;
552
552
553 if (loc.length > 1) {
553 if (loc.length > 1) {
554
554
555 var highlightable_line_tds = [];
555 var highlightable_line_tds = [];
556
556
557 // source code line format
557 // source code line format
558 var page_highlights = loc.substring(loc.indexOf('#') + 1).split('L');
558 var page_highlights = loc.substring(loc.indexOf('#') + 1).split('L');
559
559
560 // multi-line HL, for files
560 // multi-line HL, for files
561 if (page_highlights.length > 1) {
561 if (page_highlights.length > 1) {
562 var highlight_ranges = page_highlights[1].split(",");
562 var highlight_ranges = page_highlights[1].split(",");
563 var h_lines = [];
563 var h_lines = [];
564 for (var pos in highlight_ranges) {
564 for (var pos in highlight_ranges) {
565 var _range = highlight_ranges[pos].split('-');
565 var _range = highlight_ranges[pos].split('-');
566 if (_range.length === 2) {
566 if (_range.length === 2) {
567 var start = parseInt(_range[0]);
567 var start = parseInt(_range[0]);
568 var end = parseInt(_range[1]);
568 var end = parseInt(_range[1]);
569 if (start < end) {
569 if (start < end) {
570 for (var i = start; i <= end; i++) {
570 for (var i = start; i <= end; i++) {
571 h_lines.push(i);
571 h_lines.push(i);
572 }
572 }
573 }
573 }
574 } else {
574 } else {
575 h_lines.push(parseInt(highlight_ranges[pos]));
575 h_lines.push(parseInt(highlight_ranges[pos]));
576 }
576 }
577 }
577 }
578 for (pos in h_lines) {
578 for (pos in h_lines) {
579 var line_td = $('td.cb-lineno#L' + h_lines[pos]);
579 var line_td = $('td.cb-lineno#L' + h_lines[pos]);
580 if (line_td.length) {
580 if (line_td.length) {
581 highlightable_line_tds.push(line_td);
581 highlightable_line_tds.push(line_td);
582 }
582 }
583 }
583 }
584 }
584 }
585
585
586 // now check a direct id reference of line in diff / pull-request page)
586 // now check a direct id reference of line in diff / pull-request page)
587 if ($(loc).length > 0 && $(loc).hasClass('cb-lineno')) {
587 if ($(loc).length > 0 && $(loc).hasClass('cb-lineno')) {
588 highlightable_line_tds.push($(loc));
588 highlightable_line_tds.push($(loc));
589 }
589 }
590
590
591 // mark diff lines as selected
591 // mark diff lines as selected
592 $.each(highlightable_line_tds, function (i, $td) {
592 $.each(highlightable_line_tds, function (i, $td) {
593 $td.addClass('cb-line-selected'); // line number td
593 $td.addClass('cb-line-selected'); // line number td
594 $td.prev().addClass('cb-line-selected'); // line data
594 $td.prev().addClass('cb-line-selected'); // line data
595 $td.next().addClass('cb-line-selected'); // line content
595 $td.next().addClass('cb-line-selected'); // line content
596 });
596 });
597
597
598 if (highlightable_line_tds.length > 0) {
598 if (highlightable_line_tds.length > 0) {
599 var $first_line_td = highlightable_line_tds[0];
599 var $first_line_td = highlightable_line_tds[0];
600 scrollToElement($first_line_td);
600 scrollToElement($first_line_td);
601 $.Topic('/ui/plugins/code/anchor_focus').prepareOrPublish({
601 $.Topic('/ui/plugins/code/anchor_focus').prepareOrPublish({
602 td: $first_line_td,
602 td: $first_line_td,
603 remainder: result.remainder
603 remainder: result.remainder
604 });
604 });
605 } else {
605 } else {
606 // case for direct anchor to comments
606 // case for direct anchor to comments
607 var $line = $(loc);
607 var $line = $(loc);
608
608
609 if ($line.hasClass('comment-general')) {
609 if ($line.hasClass('comment-general')) {
610 $line.show();
610 $line.show();
611 } else if ($line.hasClass('comment-inline')) {
611 } else if ($line.hasClass('comment-inline')) {
612 $line.show();
612 $line.show();
613 var $cb = $line.closest('.cb');
613 var $cb = $line.closest('.cb');
614 $cb.removeClass('cb-collapsed')
614 $cb.removeClass('cb-collapsed')
615 }
615 }
616 if ($line.length > 0) {
616 if ($line.length > 0) {
617 $line.addClass('comment-selected-hl');
617 $line.addClass('comment-selected-hl');
618 offsetScroll($line, 70);
618 offsetScroll($line, 70);
619 }
619 }
620 if (!$line.hasClass('comment-outdated') && result.remainder === '/ReplyToComment') {
620 if (!$line.hasClass('comment-outdated') && result.remainder === '/ReplyToComment') {
621 $line.parent().find('.cb-comment-add-button').trigger('click');
621 $line.parent().find('.cb-comment-add-button').trigger('click');
622 }
622 }
623 }
623 }
624
624
625 }
625 }
626 }
626 }
627 collapsableContent();
627 collapsableContent();
628 });
628 });
629
629
630 var feedLifetimeOptions = function(query, initialData){
630 var feedLifetimeOptions = function(query, initialData){
631 var data = {results: []};
631 var data = {results: []};
632 var isQuery = typeof query.term !== 'undefined';
632 var isQuery = typeof query.term !== 'undefined';
633
633
634 var section = _gettext('Lifetime');
634 var section = _gettext('Lifetime');
635 var children = [];
635 var children = [];
636
636
637 //filter results
637 //filter results
638 $.each(initialData.results, function(idx, value) {
638 $.each(initialData.results, function(idx, value) {
639
639
640 if (!isQuery || query.term.length === 0 || value.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0) {
640 if (!isQuery || query.term.length === 0 || value.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0) {
641 children.push({
641 children.push({
642 'id': this.id,
642 'id': this.id,
643 'text': this.text
643 'text': this.text
644 })
644 })
645 }
645 }
646
646
647 });
647 });
648 data.results.push({
648 data.results.push({
649 'text': section,
649 'text': section,
650 'children': children
650 'children': children
651 });
651 });
652
652
653 if (isQuery) {
653 if (isQuery) {
654
654
655 var now = moment.utc();
655 var now = moment.utc();
656
656
657 var parseQuery = function(entry, now){
657 var parseQuery = function(entry, now){
658 var fmt = 'DD/MM/YYYY H:mm';
658 var fmt = 'DD/MM/YYYY H:mm';
659 var parsed = moment.utc(entry, fmt);
659 var parsed = moment.utc(entry, fmt);
660 var diffInMin = parsed.diff(now, 'minutes');
660 var diffInMin = parsed.diff(now, 'minutes');
661
661
662 if (diffInMin > 0){
662 if (diffInMin > 0){
663 return {
663 return {
664 id: diffInMin,
664 id: diffInMin,
665 text: parsed.format(fmt)
665 text: parsed.format(fmt)
666 }
666 }
667 } else {
667 } else {
668 return {
668 return {
669 id: undefined,
669 id: undefined,
670 text: parsed.format('DD/MM/YYYY') + ' ' + _gettext('date not in future')
670 text: parsed.format('DD/MM/YYYY') + ' ' + _gettext('date not in future')
671 }
671 }
672 }
672 }
673
673
674
674
675 };
675 };
676
676
677 data.results.push({
677 data.results.push({
678 'text': _gettext('Specified expiration date'),
678 'text': _gettext('Specified expiration date'),
679 'children': [{
679 'children': [{
680 'id': parseQuery(query.term, now).id,
680 'id': parseQuery(query.term, now).id,
681 'text': parseQuery(query.term, now).text
681 'text': parseQuery(query.term, now).text
682 }]
682 }]
683 });
683 });
684 }
684 }
685
685
686 query.callback(data);
686 query.callback(data);
687 };
687 };
688
688
689 /*
689 /*
690 * Retrievew via templateContext.session_attrs.key
690 * Retrievew via templateContext.session_attrs.key
691 * */
691 * */
692 var storeUserSessionAttr = function (key, val) {
692 var storeUserSessionAttr = function (key, val) {
693
693
694 var postData = {
694 var postData = {
695 'key': key,
695 'key': key,
696 'val': val,
696 'val': val,
697 'csrf_token': CSRF_TOKEN
697 'csrf_token': CSRF_TOKEN
698 };
698 };
699
699
700 var success = function(o) {
700 var success = function(o) {
701 return true
701 return true
702 };
702 };
703
703
704 ajaxPOST(pyroutes.url('store_user_session_value'), postData, success);
704 ajaxPOST(pyroutes.url('store_user_session_value'), postData, success);
705 return false;
705 return false;
706 };
706 };
707
707
708
708
709 var getUserSessionAttr = function(key) {
709 var getUserSessionAttr = function(key) {
710 var storeKey = templateContext.session_attrs;
710 var storeKey = templateContext.session_attrs;
711 var val = storeKey[key]
711 var val = storeKey[key]
712 if (val !== undefined) {
712 if (val !== undefined) {
713 return JSON.parse(val)
713 return JSON.parse(val)
714 }
714 }
715 return null
715 return null
716 }
716 }
717
718 window.scrollDown = function () {
719 $(document).scrollTop($(document).height());
720 }
721
722 window.scrollUp = function scrollUp() {
723 $(window).scrollTop(0);
724 }
@@ -1,1170 +1,1173 b''
1 // # Copyright (C) 2010-2020 RhodeCode GmbH
1 // # Copyright (C) 2010-2020 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
5 // # (only), as published by the Free Software Foundation.
5 // # (only), as published by the Free Software Foundation.
6 // #
6 // #
7 // # This program is distributed in the hope that it will be useful,
7 // # This program is distributed in the hope that it will be useful,
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // # GNU General Public License for more details.
10 // # GNU General Public License for more details.
11 // #
11 // #
12 // # You should have received a copy of the GNU Affero General Public License
12 // # You should have received a copy of the GNU Affero General Public License
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 // #
14 // #
15 // # This program is dual-licensed. If you wish to learn more about the
15 // # This program is dual-licensed. If you wish to learn more about the
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19
19
20 var prButtonLockChecks = {
20 var prButtonLockChecks = {
21 'compare': false,
21 'compare': false,
22 'reviewers': false
22 'reviewers': false
23 };
23 };
24
24
25 /**
25 /**
26 * lock button until all checks and loads are made. E.g reviewer calculation
26 * lock button until all checks and loads are made. E.g reviewer calculation
27 * should prevent from submitting a PR
27 * should prevent from submitting a PR
28 * @param lockEnabled
28 * @param lockEnabled
29 * @param msg
29 * @param msg
30 * @param scope
30 * @param scope
31 */
31 */
32 var prButtonLock = function(lockEnabled, msg, scope) {
32 var prButtonLock = function(lockEnabled, msg, scope) {
33 scope = scope || 'all';
33 scope = scope || 'all';
34 if (scope == 'all'){
34 if (scope == 'all'){
35 prButtonLockChecks['compare'] = !lockEnabled;
35 prButtonLockChecks['compare'] = !lockEnabled;
36 prButtonLockChecks['reviewers'] = !lockEnabled;
36 prButtonLockChecks['reviewers'] = !lockEnabled;
37 } else if (scope == 'compare') {
37 } else if (scope == 'compare') {
38 prButtonLockChecks['compare'] = !lockEnabled;
38 prButtonLockChecks['compare'] = !lockEnabled;
39 } else if (scope == 'reviewers'){
39 } else if (scope == 'reviewers'){
40 prButtonLockChecks['reviewers'] = !lockEnabled;
40 prButtonLockChecks['reviewers'] = !lockEnabled;
41 }
41 }
42 var checksMeet = prButtonLockChecks.compare && prButtonLockChecks.reviewers;
42 var checksMeet = prButtonLockChecks.compare && prButtonLockChecks.reviewers;
43 if (lockEnabled) {
43 if (lockEnabled) {
44 $('#pr_submit').attr('disabled', 'disabled');
44 $('#pr_submit').attr('disabled', 'disabled');
45 }
45 }
46 else if (checksMeet) {
46 else if (checksMeet) {
47 $('#pr_submit').removeAttr('disabled');
47 $('#pr_submit').removeAttr('disabled');
48 }
48 }
49
49
50 if (msg) {
50 if (msg) {
51 $('#pr_open_message').html(msg);
51 $('#pr_open_message').html(msg);
52 }
52 }
53 };
53 };
54
54
55
55
56 /**
56 /**
57 Generate Title and Description for a PullRequest.
57 Generate Title and Description for a PullRequest.
58 In case of 1 commits, the title and description is that one commit
58 In case of 1 commits, the title and description is that one commit
59 in case of multiple commits, we iterate on them with max N number of commits,
59 in case of multiple commits, we iterate on them with max N number of commits,
60 and build description in a form
60 and build description in a form
61 - commitN
61 - commitN
62 - commitN+1
62 - commitN+1
63 ...
63 ...
64
64
65 Title is then constructed from branch names, or other references,
65 Title is then constructed from branch names, or other references,
66 replacing '-' and '_' into spaces
66 replacing '-' and '_' into spaces
67
67
68 * @param sourceRef
68 * @param sourceRef
69 * @param elements
69 * @param elements
70 * @param limit
70 * @param limit
71 * @returns {*[]}
71 * @returns {*[]}
72 */
72 */
73 var getTitleAndDescription = function(sourceRefType, sourceRef, elements, limit) {
73 var getTitleAndDescription = function(sourceRefType, sourceRef, elements, limit) {
74 var title = '';
74 var title = '';
75 var desc = '';
75 var desc = '';
76
76
77 $.each($(elements).get().reverse().slice(0, limit), function(idx, value) {
77 $.each($(elements).get().reverse().slice(0, limit), function(idx, value) {
78 var rawMessage = value['message'];
78 var rawMessage = value['message'];
79 desc += '- ' + rawMessage.split('\n')[0].replace(/\n+$/, "") + '\n';
79 desc += '- ' + rawMessage.split('\n')[0].replace(/\n+$/, "") + '\n';
80 });
80 });
81 // only 1 commit, use commit message as title
81 // only 1 commit, use commit message as title
82 if (elements.length === 1) {
82 if (elements.length === 1) {
83 var rawMessage = elements[0]['message'];
83 var rawMessage = elements[0]['message'];
84 title = rawMessage.split('\n')[0];
84 title = rawMessage.split('\n')[0];
85 }
85 }
86 else {
86 else {
87 // use reference name
87 // use reference name
88 var normalizedRef = sourceRef.replace(/-/g, ' ').replace(/_/g, ' ').capitalizeFirstLetter()
88 var normalizedRef = sourceRef.replace(/-/g, ' ').replace(/_/g, ' ').capitalizeFirstLetter()
89 var refType = sourceRefType;
89 var refType = sourceRefType;
90 title = 'Changes from {0}: {1}'.format(refType, normalizedRef);
90 title = 'Changes from {0}: {1}'.format(refType, normalizedRef);
91 }
91 }
92
92
93 return [title, desc]
93 return [title, desc]
94 };
94 };
95
95
96
96
97 window.ReviewersController = function () {
97 window.ReviewersController = function () {
98 var self = this;
98 var self = this;
99 this.$loadingIndicator = $('.calculate-reviewers');
99 this.$loadingIndicator = $('.calculate-reviewers');
100 this.$reviewRulesContainer = $('#review_rules');
100 this.$reviewRulesContainer = $('#review_rules');
101 this.$rulesList = this.$reviewRulesContainer.find('.pr-reviewer-rules');
101 this.$rulesList = this.$reviewRulesContainer.find('.pr-reviewer-rules');
102 this.$userRule = $('.pr-user-rule-container');
102 this.$userRule = $('.pr-user-rule-container');
103 this.$reviewMembers = $('#review_members');
103 this.$reviewMembers = $('#review_members');
104 this.$observerMembers = $('#observer_members');
104 this.$observerMembers = $('#observer_members');
105
105
106 this.currentRequest = null;
106 this.currentRequest = null;
107 this.diffData = null;
107 this.diffData = null;
108 this.enabledRules = [];
108 this.enabledRules = [];
109 // sync with db.py entries
109 // sync with db.py entries
110 this.ROLE_REVIEWER = 'reviewer';
110 this.ROLE_REVIEWER = 'reviewer';
111 this.ROLE_OBSERVER = 'observer'
111 this.ROLE_OBSERVER = 'observer'
112
112
113 //dummy handler, we might register our own later
113 //dummy handler, we might register our own later
114 this.diffDataHandler = function (data) {};
114 this.diffDataHandler = function (data) {};
115
115
116 this.defaultForbidUsers = function () {
116 this.defaultForbidUsers = function () {
117 return [
117 return [
118 {
118 {
119 'username': 'default',
119 'username': 'default',
120 'user_id': templateContext.default_user.user_id
120 'user_id': templateContext.default_user.user_id
121 }
121 }
122 ];
122 ];
123 };
123 };
124
124
125 // init default forbidden users
125 // init default forbidden users
126 this.forbidUsers = this.defaultForbidUsers();
126 this.forbidUsers = this.defaultForbidUsers();
127
127
128 this.hideReviewRules = function () {
128 this.hideReviewRules = function () {
129 self.$reviewRulesContainer.hide();
129 self.$reviewRulesContainer.hide();
130 $(self.$userRule.selector).hide();
130 $(self.$userRule.selector).hide();
131 };
131 };
132
132
133 this.showReviewRules = function () {
133 this.showReviewRules = function () {
134 self.$reviewRulesContainer.show();
134 self.$reviewRulesContainer.show();
135 $(self.$userRule.selector).show();
135 $(self.$userRule.selector).show();
136 };
136 };
137
137
138 this.addRule = function (ruleText) {
138 this.addRule = function (ruleText) {
139 self.showReviewRules();
139 self.showReviewRules();
140 self.enabledRules.push(ruleText);
140 self.enabledRules.push(ruleText);
141 return '<div>- {0}</div>'.format(ruleText)
141 return '<div>- {0}</div>'.format(ruleText)
142 };
142 };
143
143
144 this.increaseCounter = function(role) {
144 this.increaseCounter = function(role) {
145 if (role === self.ROLE_REVIEWER) {
145 if (role === self.ROLE_REVIEWER) {
146 var $elem = $('#reviewers-cnt')
146 var $elem = $('#reviewers-cnt')
147 var cnt = parseInt($elem.data('count') || 0)
147 var cnt = parseInt($elem.data('count') || 0)
148 cnt +=1
148 cnt +=1
149 $elem.html(cnt);
149 $elem.html(cnt);
150 $elem.data('count', cnt);
150 $elem.data('count', cnt);
151 }
151 }
152 else if (role === self.ROLE_OBSERVER) {
152 else if (role === self.ROLE_OBSERVER) {
153 var $elem = $('#observers-cnt');
153 var $elem = $('#observers-cnt');
154 var cnt = parseInt($elem.data('count') || 0)
154 var cnt = parseInt($elem.data('count') || 0)
155 cnt +=1
155 cnt +=1
156 $elem.html(cnt);
156 $elem.html(cnt);
157 $elem.data('count', cnt);
157 $elem.data('count', cnt);
158 }
158 }
159 }
159 }
160
160
161 this.resetCounter = function () {
161 this.resetCounter = function () {
162 var $elem = $('#reviewers-cnt');
162 var $elem = $('#reviewers-cnt');
163
163
164 $elem.data('count', 0);
164 $elem.data('count', 0);
165 $elem.html(0);
165 $elem.html(0);
166
166
167 var $elem = $('#observers-cnt');
167 var $elem = $('#observers-cnt');
168
168
169 $elem.data('count', 0);
169 $elem.data('count', 0);
170 $elem.html(0);
170 $elem.html(0);
171 }
171 }
172
172
173 this.loadReviewRules = function (data) {
173 this.loadReviewRules = function (data) {
174 self.diffData = data;
174 self.diffData = data;
175
175
176 // reset forbidden Users
176 // reset forbidden Users
177 this.forbidUsers = self.defaultForbidUsers();
177 this.forbidUsers = self.defaultForbidUsers();
178
178
179 // reset state of review rules
179 // reset state of review rules
180 self.$rulesList.html('');
180 self.$rulesList.html('');
181
181
182 if (!data || data.rules === undefined || $.isEmptyObject(data.rules)) {
182 if (!data || data.rules === undefined || $.isEmptyObject(data.rules)) {
183 // default rule, case for older repo that don't have any rules stored
183 // default rule, case for older repo that don't have any rules stored
184 self.$rulesList.append(
184 self.$rulesList.append(
185 self.addRule(_gettext('All reviewers must vote.'))
185 self.addRule(_gettext('All reviewers must vote.'))
186 );
186 );
187 return self.forbidUsers
187 return self.forbidUsers
188 }
188 }
189
189
190 if (data.rules.forbid_adding_reviewers) {
190 if (data.rules.forbid_adding_reviewers) {
191 $('#add_reviewer_input').remove();
191 $('#add_reviewer_input').remove();
192 }
192 }
193
193
194 if (data.rules_data !== undefined && data.rules_data.forbidden_users !== undefined) {
194 if (data.rules_data !== undefined && data.rules_data.forbidden_users !== undefined) {
195 $.each(data.rules_data.forbidden_users, function(idx, val){
195 $.each(data.rules_data.forbidden_users, function(idx, val){
196 self.forbidUsers.push(val)
196 self.forbidUsers.push(val)
197 })
197 })
198 }
198 }
199
199
200 if (data.rules_humanized !== undefined && data.rules_humanized.length > 0) {
200 if (data.rules_humanized !== undefined && data.rules_humanized.length > 0) {
201 $.each(data.rules_humanized, function(idx, val) {
201 $.each(data.rules_humanized, function(idx, val) {
202 self.$rulesList.append(
202 self.$rulesList.append(
203 self.addRule(val)
203 self.addRule(val)
204 )
204 )
205 })
205 })
206 } else {
206 } else {
207 // we don't have any rules set, so we inform users about it
207 // we don't have any rules set, so we inform users about it
208 self.$rulesList.append(
208 self.$rulesList.append(
209 self.addRule(_gettext('No additional review rules set.'))
209 self.addRule(_gettext('No additional review rules set.'))
210 )
210 )
211 }
211 }
212
212
213 return self.forbidUsers
213 return self.forbidUsers
214 };
214 };
215
215
216 this.emptyTables = function () {
216 this.emptyTables = function () {
217 self.emptyReviewersTable();
217 self.emptyReviewersTable();
218 self.emptyObserversTable();
218 self.emptyObserversTable();
219
219
220 // Also reset counters.
220 // Also reset counters.
221 self.resetCounter();
221 self.resetCounter();
222 }
222 }
223
223
224 this.emptyReviewersTable = function (withText) {
224 this.emptyReviewersTable = function (withText) {
225 self.$reviewMembers.empty();
225 self.$reviewMembers.empty();
226 if (withText !== undefined) {
226 if (withText !== undefined) {
227 self.$reviewMembers.html(withText)
227 self.$reviewMembers.html(withText)
228 }
228 }
229 };
229 };
230
230
231 this.emptyObserversTable = function (withText) {
231 this.emptyObserversTable = function (withText) {
232 self.$observerMembers.empty();
232 self.$observerMembers.empty();
233 if (withText !== undefined) {
233 if (withText !== undefined) {
234 self.$observerMembers.html(withText)
234 self.$observerMembers.html(withText)
235 }
235 }
236 }
236 }
237
237
238 this.loadDefaultReviewers = function (sourceRepo, sourceRef, targetRepo, targetRef) {
238 this.loadDefaultReviewers = function (sourceRepo, sourceRef, targetRepo, targetRef) {
239
239
240 if (self.currentRequest) {
240 if (self.currentRequest) {
241 // make sure we cleanup old running requests before triggering this again
241 // make sure we cleanup old running requests before triggering this again
242 self.currentRequest.abort();
242 self.currentRequest.abort();
243 }
243 }
244
244
245 self.$loadingIndicator.show();
245 self.$loadingIndicator.show();
246
246
247 // reset reviewer/observe members
247 // reset reviewer/observe members
248 self.emptyTables();
248 self.emptyTables();
249
249
250 prButtonLock(true, null, 'reviewers');
250 prButtonLock(true, null, 'reviewers');
251 $('#user').hide(); // hide user autocomplete before load
251 $('#user').hide(); // hide user autocomplete before load
252 $('#observer').hide(); //hide observer autocomplete before load
252 $('#observer').hide(); //hide observer autocomplete before load
253
253
254 // lock PR button, so we cannot send PR before it's calculated
254 // lock PR button, so we cannot send PR before it's calculated
255 prButtonLock(true, _gettext('Loading diff ...'), 'compare');
255 prButtonLock(true, _gettext('Loading diff ...'), 'compare');
256
256
257 if (sourceRef.length !== 3 || targetRef.length !== 3) {
257 if (sourceRef.length !== 3 || targetRef.length !== 3) {
258 // don't load defaults in case we're missing some refs...
258 // don't load defaults in case we're missing some refs...
259 self.$loadingIndicator.hide();
259 self.$loadingIndicator.hide();
260 return
260 return
261 }
261 }
262
262
263 var url = pyroutes.url('repo_default_reviewers_data',
263 var url = pyroutes.url('repo_default_reviewers_data',
264 {
264 {
265 'repo_name': templateContext.repo_name,
265 'repo_name': templateContext.repo_name,
266 'source_repo': sourceRepo,
266 'source_repo': sourceRepo,
267 'source_ref_type': sourceRef[0],
267 'source_ref_type': sourceRef[0],
268 'source_ref_name': sourceRef[1],
268 'source_ref_name': sourceRef[1],
269 'source_ref': sourceRef[2],
269 'source_ref': sourceRef[2],
270 'target_repo': targetRepo,
270 'target_repo': targetRepo,
271 'target_ref': targetRef[2],
271 'target_ref': targetRef[2],
272 'target_ref_type': targetRef[0],
272 'target_ref_type': targetRef[0],
273 'target_ref_name': targetRef[1]
273 'target_ref_name': targetRef[1]
274 });
274 });
275
275
276 self.currentRequest = $.ajax({
276 self.currentRequest = $.ajax({
277 url: url,
277 url: url,
278 headers: {'X-PARTIAL-XHR': true},
278 headers: {'X-PARTIAL-XHR': true},
279 type: 'GET',
279 type: 'GET',
280 success: function (data) {
280 success: function (data) {
281
281
282 self.currentRequest = null;
282 self.currentRequest = null;
283
283
284 // review rules
284 // review rules
285 self.loadReviewRules(data);
285 self.loadReviewRules(data);
286 var diffHandled = self.handleDiffData(data["diff_info"]);
286 var diffHandled = self.handleDiffData(data["diff_info"]);
287 if (diffHandled === false) {
287 if (diffHandled === false) {
288 return
288 return
289 }
289 }
290
290
291 for (var i = 0; i < data.reviewers.length; i++) {
291 for (var i = 0; i < data.reviewers.length; i++) {
292 var reviewer = data.reviewers[i];
292 var reviewer = data.reviewers[i];
293 // load reviewer rules from the repo data
293 // load reviewer rules from the repo data
294 self.addMember(reviewer, reviewer.reasons, reviewer.mandatory, reviewer.role);
294 self.addMember(reviewer, reviewer.reasons, reviewer.mandatory, reviewer.role);
295 }
295 }
296
296
297
297
298 self.$loadingIndicator.hide();
298 self.$loadingIndicator.hide();
299 prButtonLock(false, null, 'reviewers');
299 prButtonLock(false, null, 'reviewers');
300
300
301 $('#user').show(); // show user autocomplete before load
301 $('#user').show(); // show user autocomplete before load
302 $('#observer').show(); // show observer autocomplete before load
302 $('#observer').show(); // show observer autocomplete before load
303
303
304 var commitElements = data["diff_info"]['commits'];
304 var commitElements = data["diff_info"]['commits'];
305
305
306 if (commitElements.length === 0) {
306 if (commitElements.length === 0) {
307 var noCommitsMsg = '<span class="alert-text-warning">{0}</span>'.format(
307 var noCommitsMsg = '<span class="alert-text-warning">{0}</span>'.format(
308 _gettext('There are no commits to merge.'));
308 _gettext('There are no commits to merge.'));
309 prButtonLock(true, noCommitsMsg, 'all');
309 prButtonLock(true, noCommitsMsg, 'all');
310
310
311 } else {
311 } else {
312 // un-lock PR button, so we cannot send PR before it's calculated
312 // un-lock PR button, so we cannot send PR before it's calculated
313 prButtonLock(false, null, 'compare');
313 prButtonLock(false, null, 'compare');
314 }
314 }
315
315
316 },
316 },
317 error: function (jqXHR, textStatus, errorThrown) {
317 error: function (jqXHR, textStatus, errorThrown) {
318 var prefix = "Loading diff and reviewers/observers failed\n"
318 var prefix = "Loading diff and reviewers/observers failed\n"
319 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
319 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
320 ajaxErrorSwal(message);
320 ajaxErrorSwal(message);
321 }
321 }
322 });
322 });
323
323
324 };
324 };
325
325
326 // check those, refactor
326 // check those, refactor
327 this.removeMember = function (reviewer_id, mark_delete) {
327 this.removeMember = function (reviewer_id, mark_delete) {
328 var reviewer = $('#reviewer_{0}'.format(reviewer_id));
328 var reviewer = $('#reviewer_{0}'.format(reviewer_id));
329
329
330 if (typeof (mark_delete) === undefined) {
330 if (typeof (mark_delete) === undefined) {
331 mark_delete = false;
331 mark_delete = false;
332 }
332 }
333
333
334 if (mark_delete === true) {
334 if (mark_delete === true) {
335 if (reviewer) {
335 if (reviewer) {
336 // now delete the input
336 // now delete the input
337 $('#reviewer_{0} input'.format(reviewer_id)).remove();
337 $('#reviewer_{0} input'.format(reviewer_id)).remove();
338 $('#reviewer_{0}_rules input'.format(reviewer_id)).remove();
338 $('#reviewer_{0}_rules input'.format(reviewer_id)).remove();
339 // mark as to-delete
339 // mark as to-delete
340 var obj = $('#reviewer_{0}_name'.format(reviewer_id));
340 var obj = $('#reviewer_{0}_name'.format(reviewer_id));
341 obj.addClass('to-delete');
341 obj.addClass('to-delete');
342 obj.css({"text-decoration": "line-through", "opacity": 0.5});
342 obj.css({"text-decoration": "line-through", "opacity": 0.5});
343 }
343 }
344 } else {
344 } else {
345 $('#reviewer_{0}'.format(reviewer_id)).remove();
345 $('#reviewer_{0}'.format(reviewer_id)).remove();
346 }
346 }
347 };
347 };
348
348
349 this.addMember = function (reviewer_obj, reasons, mandatory, role) {
349 this.addMember = function (reviewer_obj, reasons, mandatory, role) {
350
350
351 var id = reviewer_obj.user_id;
351 var id = reviewer_obj.user_id;
352 var username = reviewer_obj.username;
352 var username = reviewer_obj.username;
353
353
354 reasons = reasons || [];
354 reasons = reasons || [];
355 mandatory = mandatory || false;
355 mandatory = mandatory || false;
356 role = role || self.ROLE_REVIEWER
356 role = role || self.ROLE_REVIEWER
357
357
358 // register current set IDS to check if we don't have this ID already in
358 // register current set IDS to check if we don't have this ID already in
359 // and prevent duplicates
359 // and prevent duplicates
360 var currentIds = [];
360 var currentIds = [];
361
361
362 $.each($('.reviewer_entry'), function (index, value) {
362 $.each($('.reviewer_entry'), function (index, value) {
363 currentIds.push($(value).data('reviewerUserId'))
363 currentIds.push($(value).data('reviewerUserId'))
364 })
364 })
365
365
366 var userAllowedReview = function (userId) {
366 var userAllowedReview = function (userId) {
367 var allowed = true;
367 var allowed = true;
368 $.each(self.forbidUsers, function (index, member_data) {
368 $.each(self.forbidUsers, function (index, member_data) {
369 if (parseInt(userId) === member_data['user_id']) {
369 if (parseInt(userId) === member_data['user_id']) {
370 allowed = false;
370 allowed = false;
371 return false // breaks the loop
371 return false // breaks the loop
372 }
372 }
373 });
373 });
374 return allowed
374 return allowed
375 };
375 };
376
376
377 var userAllowed = userAllowedReview(id);
377 var userAllowed = userAllowedReview(id);
378
378
379 if (!userAllowed) {
379 if (!userAllowed) {
380 alert(_gettext('User `{0}` not allowed to be a reviewer').format(username));
380 alert(_gettext('User `{0}` not allowed to be a reviewer').format(username));
381 } else {
381 } else {
382 // only add if it's not there
382 // only add if it's not there
383 var alreadyReviewer = currentIds.indexOf(id) != -1;
383 var alreadyReviewer = currentIds.indexOf(id) != -1;
384
384
385 if (alreadyReviewer) {
385 if (alreadyReviewer) {
386 alert(_gettext('User `{0}` already in reviewers/observers').format(username));
386 alert(_gettext('User `{0}` already in reviewers/observers').format(username));
387 } else {
387 } else {
388
388
389 var reviewerEntry = renderTemplate('reviewMemberEntry', {
389 var reviewerEntry = renderTemplate('reviewMemberEntry', {
390 'member': reviewer_obj,
390 'member': reviewer_obj,
391 'mandatory': mandatory,
391 'mandatory': mandatory,
392 'role': role,
392 'role': role,
393 'reasons': reasons,
393 'reasons': reasons,
394 'allowed_to_update': true,
394 'allowed_to_update': true,
395 'review_status': 'not_reviewed',
395 'review_status': 'not_reviewed',
396 'review_status_label': _gettext('Not Reviewed'),
396 'review_status_label': _gettext('Not Reviewed'),
397 'user_group': reviewer_obj.user_group,
397 'user_group': reviewer_obj.user_group,
398 'create': true,
398 'create': true,
399 'rule_show': true,
399 'rule_show': true,
400 'rhodecode_user': templateContext.rhodecode_user
400 })
401 })
401
402
402 if (role === self.ROLE_REVIEWER) {
403 if (role === self.ROLE_REVIEWER) {
403 $(self.$reviewMembers.selector).append(reviewerEntry);
404 $(self.$reviewMembers.selector).append(reviewerEntry);
404 self.increaseCounter(self.ROLE_REVIEWER);
405 self.increaseCounter(self.ROLE_REVIEWER);
405 $('#reviewer-empty-msg').remove()
406 $('#reviewer-empty-msg').remove()
406 }
407 }
407 else if (role === self.ROLE_OBSERVER) {
408 else if (role === self.ROLE_OBSERVER) {
408 $(self.$observerMembers.selector).append(reviewerEntry);
409 $(self.$observerMembers.selector).append(reviewerEntry);
409 self.increaseCounter(self.ROLE_OBSERVER);
410 self.increaseCounter(self.ROLE_OBSERVER);
410 $('#observer-empty-msg').remove();
411 $('#observer-empty-msg').remove();
411 }
412 }
412
413
413 tooltipActivate();
414 tooltipActivate();
414 }
415 }
415 }
416 }
416
417
417 };
418 };
418
419
419 this.updateReviewers = function (repo_name, pull_request_id, role) {
420 this.updateReviewers = function (repo_name, pull_request_id, role) {
420 if (role === 'reviewer') {
421 if (role === 'reviewer') {
421 var postData = $('#reviewers input').serialize();
422 var postData = $('#reviewers input').serialize();
422 _updatePullRequest(repo_name, pull_request_id, postData);
423 _updatePullRequest(repo_name, pull_request_id, postData);
423 } else if (role === 'observer') {
424 } else if (role === 'observer') {
424 var postData = $('#observers input').serialize();
425 var postData = $('#observers input').serialize();
425 _updatePullRequest(repo_name, pull_request_id, postData);
426 _updatePullRequest(repo_name, pull_request_id, postData);
426 }
427 }
427 };
428 };
428
429
429 this.handleDiffData = function (data) {
430 this.handleDiffData = function (data) {
430 return self.diffDataHandler(data)
431 return self.diffDataHandler(data)
431 }
432 }
432 };
433 };
433
434
434
435
435 var _updatePullRequest = function(repo_name, pull_request_id, postData) {
436 var _updatePullRequest = function(repo_name, pull_request_id, postData) {
436 var url = pyroutes.url(
437 var url = pyroutes.url(
437 'pullrequest_update',
438 'pullrequest_update',
438 {"repo_name": repo_name, "pull_request_id": pull_request_id});
439 {"repo_name": repo_name, "pull_request_id": pull_request_id});
439 if (typeof postData === 'string' ) {
440 if (typeof postData === 'string' ) {
440 postData += '&csrf_token=' + CSRF_TOKEN;
441 postData += '&csrf_token=' + CSRF_TOKEN;
441 } else {
442 } else {
442 postData.csrf_token = CSRF_TOKEN;
443 postData.csrf_token = CSRF_TOKEN;
443 }
444 }
444
445
445 var success = function(o) {
446 var success = function(o) {
446 var redirectUrl = o['redirect_url'];
447 var redirectUrl = o['redirect_url'];
447 if (redirectUrl !== undefined && redirectUrl !== null && redirectUrl !== '') {
448 if (redirectUrl !== undefined && redirectUrl !== null && redirectUrl !== '') {
448 window.location = redirectUrl;
449 window.location = redirectUrl;
449 } else {
450 } else {
450 window.location.reload();
451 window.location.reload();
451 }
452 }
452 };
453 };
453
454
454 ajaxPOST(url, postData, success);
455 ajaxPOST(url, postData, success);
455 };
456 };
456
457
457 /**
458 /**
458 * PULL REQUEST update commits
459 * PULL REQUEST update commits
459 */
460 */
460 var updateCommits = function(repo_name, pull_request_id, force) {
461 var updateCommits = function(repo_name, pull_request_id, force) {
461 var postData = {
462 var postData = {
462 'update_commits': true
463 'update_commits': true
463 };
464 };
464 if (force !== undefined && force === true) {
465 if (force !== undefined && force === true) {
465 postData['force_refresh'] = true
466 postData['force_refresh'] = true
466 }
467 }
467 _updatePullRequest(repo_name, pull_request_id, postData);
468 _updatePullRequest(repo_name, pull_request_id, postData);
468 };
469 };
469
470
470
471
471 /**
472 /**
472 * PULL REQUEST edit info
473 * PULL REQUEST edit info
473 */
474 */
474 var editPullRequest = function(repo_name, pull_request_id, title, description, renderer) {
475 var editPullRequest = function(repo_name, pull_request_id, title, description, renderer) {
475 var url = pyroutes.url(
476 var url = pyroutes.url(
476 'pullrequest_update',
477 'pullrequest_update',
477 {"repo_name": repo_name, "pull_request_id": pull_request_id});
478 {"repo_name": repo_name, "pull_request_id": pull_request_id});
478
479
479 var postData = {
480 var postData = {
480 'title': title,
481 'title': title,
481 'description': description,
482 'description': description,
482 'description_renderer': renderer,
483 'description_renderer': renderer,
483 'edit_pull_request': true,
484 'edit_pull_request': true,
484 'csrf_token': CSRF_TOKEN
485 'csrf_token': CSRF_TOKEN
485 };
486 };
486 var success = function(o) {
487 var success = function(o) {
487 window.location.reload();
488 window.location.reload();
488 };
489 };
489 ajaxPOST(url, postData, success);
490 ajaxPOST(url, postData, success);
490 };
491 };
491
492
492
493
493 /**
494 /**
494 * autocomplete handler for reviewers/observers
495 * autocomplete handler for reviewers/observers
495 */
496 */
496 var autoCompleteHandler = function (inputId, controller, role) {
497 var autoCompleteHandler = function (inputId, controller, role) {
497
498
498 return function (element, data) {
499 return function (element, data) {
499 var mandatory = false;
500 var mandatory = false;
500 var reasons = [_gettext('added manually by "{0}"').format(
501 var reasons = [_gettext('added manually by "{0}"').format(
501 templateContext.rhodecode_user.username)];
502 templateContext.rhodecode_user.username)];
502
503
503 // add whole user groups
504 // add whole user groups
504 if (data.value_type == 'user_group') {
505 if (data.value_type == 'user_group') {
505 reasons.push(_gettext('member of "{0}"').format(data.value_display));
506 reasons.push(_gettext('member of "{0}"').format(data.value_display));
506
507
507 $.each(data.members, function (index, member_data) {
508 $.each(data.members, function (index, member_data) {
508 var reviewer = member_data;
509 var reviewer = member_data;
509 reviewer['user_id'] = member_data['id'];
510 reviewer['user_id'] = member_data['id'];
510 reviewer['gravatar_link'] = member_data['icon_link'];
511 reviewer['gravatar_link'] = member_data['icon_link'];
511 reviewer['user_link'] = member_data['profile_link'];
512 reviewer['user_link'] = member_data['profile_link'];
512 reviewer['rules'] = [];
513 reviewer['rules'] = [];
513 controller.addMember(reviewer, reasons, mandatory, role);
514 controller.addMember(reviewer, reasons, mandatory, role);
514 })
515 })
515 }
516 }
516 // add single user
517 // add single user
517 else {
518 else {
518 var reviewer = data;
519 var reviewer = data;
519 reviewer['user_id'] = data['id'];
520 reviewer['user_id'] = data['id'];
520 reviewer['gravatar_link'] = data['icon_link'];
521 reviewer['gravatar_link'] = data['icon_link'];
521 reviewer['user_link'] = data['profile_link'];
522 reviewer['user_link'] = data['profile_link'];
522 reviewer['rules'] = [];
523 reviewer['rules'] = [];
523 controller.addMember(reviewer, reasons, mandatory, role);
524 controller.addMember(reviewer, reasons, mandatory, role);
524 }
525 }
525
526
526 $(inputId).val('');
527 $(inputId).val('');
527 }
528 }
528 }
529 }
529
530
530 /**
531 /**
531 * Reviewer autocomplete
532 * Reviewer autocomplete
532 */
533 */
533 var ReviewerAutoComplete = function (inputId, controller) {
534 var ReviewerAutoComplete = function (inputId, controller) {
534 var self = this;
535 var self = this;
535 self.controller = controller;
536 self.controller = controller;
536 self.inputId = inputId;
537 self.inputId = inputId;
537 var handler = autoCompleteHandler(inputId, controller, controller.ROLE_REVIEWER);
538 var handler = autoCompleteHandler(inputId, controller, controller.ROLE_REVIEWER);
538
539
539 $(inputId).autocomplete({
540 $(inputId).autocomplete({
540 serviceUrl: pyroutes.url('user_autocomplete_data'),
541 serviceUrl: pyroutes.url('user_autocomplete_data'),
541 minChars: 2,
542 minChars: 2,
542 maxHeight: 400,
543 maxHeight: 400,
543 deferRequestBy: 300, //miliseconds
544 deferRequestBy: 300, //miliseconds
544 showNoSuggestionNotice: true,
545 showNoSuggestionNotice: true,
545 tabDisabled: true,
546 tabDisabled: true,
546 autoSelectFirst: true,
547 autoSelectFirst: true,
547 params: {
548 params: {
548 user_id: templateContext.rhodecode_user.user_id,
549 user_id: templateContext.rhodecode_user.user_id,
549 user_groups: true,
550 user_groups: true,
550 user_groups_expand: true,
551 user_groups_expand: true,
551 skip_default_user: true
552 skip_default_user: true
552 },
553 },
553 formatResult: autocompleteFormatResult,
554 formatResult: autocompleteFormatResult,
554 lookupFilter: autocompleteFilterResult,
555 lookupFilter: autocompleteFilterResult,
555 onSelect: handler
556 onSelect: handler
556 });
557 });
557 };
558 };
558
559
559 /**
560 /**
560 * Observers autocomplete
561 * Observers autocomplete
561 */
562 */
562 var ObserverAutoComplete = function(inputId, controller) {
563 var ObserverAutoComplete = function(inputId, controller) {
563 var self = this;
564 var self = this;
564 self.controller = controller;
565 self.controller = controller;
565 self.inputId = inputId;
566 self.inputId = inputId;
566 var handler = autoCompleteHandler(inputId, controller, controller.ROLE_OBSERVER);
567 var handler = autoCompleteHandler(inputId, controller, controller.ROLE_OBSERVER);
567
568
568 $(inputId).autocomplete({
569 $(inputId).autocomplete({
569 serviceUrl: pyroutes.url('user_autocomplete_data'),
570 serviceUrl: pyroutes.url('user_autocomplete_data'),
570 minChars: 2,
571 minChars: 2,
571 maxHeight: 400,
572 maxHeight: 400,
572 deferRequestBy: 300, //miliseconds
573 deferRequestBy: 300, //miliseconds
573 showNoSuggestionNotice: true,
574 showNoSuggestionNotice: true,
574 tabDisabled: true,
575 tabDisabled: true,
575 autoSelectFirst: true,
576 autoSelectFirst: true,
576 params: {
577 params: {
577 user_id: templateContext.rhodecode_user.user_id,
578 user_id: templateContext.rhodecode_user.user_id,
578 user_groups: true,
579 user_groups: true,
579 user_groups_expand: true,
580 user_groups_expand: true,
580 skip_default_user: true
581 skip_default_user: true
581 },
582 },
582 formatResult: autocompleteFormatResult,
583 formatResult: autocompleteFormatResult,
583 lookupFilter: autocompleteFilterResult,
584 lookupFilter: autocompleteFilterResult,
584 onSelect: handler
585 onSelect: handler
585 });
586 });
586 }
587 }
587
588
588
589
589 window.VersionController = function () {
590 window.VersionController = function () {
590 var self = this;
591 var self = this;
591 this.$verSource = $('input[name=ver_source]');
592 this.$verSource = $('input[name=ver_source]');
592 this.$verTarget = $('input[name=ver_target]');
593 this.$verTarget = $('input[name=ver_target]');
593 this.$showVersionDiff = $('#show-version-diff');
594 this.$showVersionDiff = $('#show-version-diff');
594
595
595 this.adjustRadioSelectors = function (curNode) {
596 this.adjustRadioSelectors = function (curNode) {
596 var getVal = function (item) {
597 var getVal = function (item) {
597 if (item === 'latest') {
598 if (item === 'latest') {
598 return Number.MAX_SAFE_INTEGER
599 return Number.MAX_SAFE_INTEGER
599 }
600 }
600 else {
601 else {
601 return parseInt(item)
602 return parseInt(item)
602 }
603 }
603 };
604 };
604
605
605 var curVal = getVal($(curNode).val());
606 var curVal = getVal($(curNode).val());
606 var cleared = false;
607 var cleared = false;
607
608
608 $.each(self.$verSource, function (index, value) {
609 $.each(self.$verSource, function (index, value) {
609 var elVal = getVal($(value).val());
610 var elVal = getVal($(value).val());
610
611
611 if (elVal > curVal) {
612 if (elVal > curVal) {
612 if ($(value).is(':checked')) {
613 if ($(value).is(':checked')) {
613 cleared = true;
614 cleared = true;
614 }
615 }
615 $(value).attr('disabled', 'disabled');
616 $(value).attr('disabled', 'disabled');
616 $(value).removeAttr('checked');
617 $(value).removeAttr('checked');
617 $(value).css({'opacity': 0.1});
618 $(value).css({'opacity': 0.1});
618 }
619 }
619 else {
620 else {
620 $(value).css({'opacity': 1});
621 $(value).css({'opacity': 1});
621 $(value).removeAttr('disabled');
622 $(value).removeAttr('disabled');
622 }
623 }
623 });
624 });
624
625
625 if (cleared) {
626 if (cleared) {
626 // if we unchecked an active, set the next one to same loc.
627 // if we unchecked an active, set the next one to same loc.
627 $(this.$verSource).filter('[value={0}]'.format(
628 $(this.$verSource).filter('[value={0}]'.format(
628 curVal)).attr('checked', 'checked');
629 curVal)).attr('checked', 'checked');
629 }
630 }
630
631
631 self.setLockAction(false,
632 self.setLockAction(false,
632 $(curNode).data('verPos'),
633 $(curNode).data('verPos'),
633 $(this.$verSource).filter(':checked').data('verPos')
634 $(this.$verSource).filter(':checked').data('verPos')
634 );
635 );
635 };
636 };
636
637
637
638
638 this.attachVersionListener = function () {
639 this.attachVersionListener = function () {
639 self.$verTarget.change(function (e) {
640 self.$verTarget.change(function (e) {
640 self.adjustRadioSelectors(this)
641 self.adjustRadioSelectors(this)
641 });
642 });
642 self.$verSource.change(function (e) {
643 self.$verSource.change(function (e) {
643 self.adjustRadioSelectors(self.$verTarget.filter(':checked'))
644 self.adjustRadioSelectors(self.$verTarget.filter(':checked'))
644 });
645 });
645 };
646 };
646
647
647 this.init = function () {
648 this.init = function () {
648
649
649 var curNode = self.$verTarget.filter(':checked');
650 var curNode = self.$verTarget.filter(':checked');
650 self.adjustRadioSelectors(curNode);
651 self.adjustRadioSelectors(curNode);
651 self.setLockAction(true);
652 self.setLockAction(true);
652 self.attachVersionListener();
653 self.attachVersionListener();
653
654
654 };
655 };
655
656
656 this.setLockAction = function (state, selectedVersion, otherVersion) {
657 this.setLockAction = function (state, selectedVersion, otherVersion) {
657 var $showVersionDiff = this.$showVersionDiff;
658 var $showVersionDiff = this.$showVersionDiff;
658
659
659 if (state) {
660 if (state) {
660 $showVersionDiff.attr('disabled', 'disabled');
661 $showVersionDiff.attr('disabled', 'disabled');
661 $showVersionDiff.addClass('disabled');
662 $showVersionDiff.addClass('disabled');
662 $showVersionDiff.html($showVersionDiff.data('labelTextLocked'));
663 $showVersionDiff.html($showVersionDiff.data('labelTextLocked'));
663 }
664 }
664 else {
665 else {
665 $showVersionDiff.removeAttr('disabled');
666 $showVersionDiff.removeAttr('disabled');
666 $showVersionDiff.removeClass('disabled');
667 $showVersionDiff.removeClass('disabled');
667
668
668 if (selectedVersion == otherVersion) {
669 if (selectedVersion == otherVersion) {
669 $showVersionDiff.html($showVersionDiff.data('labelTextShow'));
670 $showVersionDiff.html($showVersionDiff.data('labelTextShow'));
670 } else {
671 } else {
671 $showVersionDiff.html($showVersionDiff.data('labelTextDiff'));
672 $showVersionDiff.html($showVersionDiff.data('labelTextDiff'));
672 }
673 }
673 }
674 }
674
675
675 };
676 };
676
677
677 this.showVersionDiff = function () {
678 this.showVersionDiff = function () {
678 var target = self.$verTarget.filter(':checked');
679 var target = self.$verTarget.filter(':checked');
679 var source = self.$verSource.filter(':checked');
680 var source = self.$verSource.filter(':checked');
680
681
681 if (target.val() && source.val()) {
682 if (target.val() && source.val()) {
682 var params = {
683 var params = {
683 'pull_request_id': templateContext.pull_request_data.pull_request_id,
684 'pull_request_id': templateContext.pull_request_data.pull_request_id,
684 'repo_name': templateContext.repo_name,
685 'repo_name': templateContext.repo_name,
685 'version': target.val(),
686 'version': target.val(),
686 'from_version': source.val()
687 'from_version': source.val()
687 };
688 };
688 window.location = pyroutes.url('pullrequest_show', params)
689 window.location = pyroutes.url('pullrequest_show', params)
689 }
690 }
690
691
691 return false;
692 return false;
692 };
693 };
693
694
694 this.toggleVersionView = function (elem) {
695 this.toggleVersionView = function (elem) {
695
696
696 if (this.$showVersionDiff.is(':visible')) {
697 if (this.$showVersionDiff.is(':visible')) {
697 $('.version-pr').hide();
698 $('.version-pr').hide();
698 this.$showVersionDiff.hide();
699 this.$showVersionDiff.hide();
699 $(elem).html($(elem).data('toggleOn'))
700 $(elem).html($(elem).data('toggleOn'))
700 } else {
701 } else {
701 $('.version-pr').show();
702 $('.version-pr').show();
702 this.$showVersionDiff.show();
703 this.$showVersionDiff.show();
703 $(elem).html($(elem).data('toggleOff'))
704 $(elem).html($(elem).data('toggleOff'))
704 }
705 }
705
706
706 return false
707 return false
707 };
708 };
708
709
709 };
710 };
710
711
711
712
712 window.UpdatePrController = function () {
713 window.UpdatePrController = function () {
713 var self = this;
714 var self = this;
714 this.$updateCommits = $('#update_commits');
715 this.$updateCommits = $('#update_commits');
715 this.$updateCommitsSwitcher = $('#update_commits_switcher');
716 this.$updateCommitsSwitcher = $('#update_commits_switcher');
716
717
717 this.lockUpdateButton = function (label) {
718 this.lockUpdateButton = function (label) {
718 self.$updateCommits.attr('disabled', 'disabled');
719 self.$updateCommits.attr('disabled', 'disabled');
719 self.$updateCommitsSwitcher.attr('disabled', 'disabled');
720 self.$updateCommitsSwitcher.attr('disabled', 'disabled');
720
721
721 self.$updateCommits.addClass('disabled');
722 self.$updateCommits.addClass('disabled');
722 self.$updateCommitsSwitcher.addClass('disabled');
723 self.$updateCommitsSwitcher.addClass('disabled');
723
724
724 self.$updateCommits.removeClass('btn-primary');
725 self.$updateCommits.removeClass('btn-primary');
725 self.$updateCommitsSwitcher.removeClass('btn-primary');
726 self.$updateCommitsSwitcher.removeClass('btn-primary');
726
727
727 self.$updateCommits.text(_gettext(label));
728 self.$updateCommits.text(_gettext(label));
728 };
729 };
729
730
730 this.isUpdateLocked = function () {
731 this.isUpdateLocked = function () {
731 return self.$updateCommits.attr('disabled') !== undefined;
732 return self.$updateCommits.attr('disabled') !== undefined;
732 };
733 };
733
734
734 this.updateCommits = function (curNode) {
735 this.updateCommits = function (curNode) {
735 if (self.isUpdateLocked()) {
736 if (self.isUpdateLocked()) {
736 return
737 return
737 }
738 }
738 self.lockUpdateButton(_gettext('Updating...'));
739 self.lockUpdateButton(_gettext('Updating...'));
739 updateCommits(
740 updateCommits(
740 templateContext.repo_name,
741 templateContext.repo_name,
741 templateContext.pull_request_data.pull_request_id);
742 templateContext.pull_request_data.pull_request_id);
742 };
743 };
743
744
744 this.forceUpdateCommits = function () {
745 this.forceUpdateCommits = function () {
745 if (self.isUpdateLocked()) {
746 if (self.isUpdateLocked()) {
746 return
747 return
747 }
748 }
748 self.lockUpdateButton(_gettext('Force updating...'));
749 self.lockUpdateButton(_gettext('Force updating...'));
749 var force = true;
750 var force = true;
750 updateCommits(
751 updateCommits(
751 templateContext.repo_name,
752 templateContext.repo_name,
752 templateContext.pull_request_data.pull_request_id, force);
753 templateContext.pull_request_data.pull_request_id, force);
753 };
754 };
754 };
755 };
755
756
756
757
757 /**
758 /**
758 * Reviewer display panel
759 * Reviewer display panel
759 */
760 */
760 window.ReviewersPanel = {
761 window.ReviewersPanel = {
761 editButton: null,
762 editButton: null,
762 closeButton: null,
763 closeButton: null,
763 addButton: null,
764 addButton: null,
764 removeButtons: null,
765 removeButtons: null,
765 reviewRules: null,
766 reviewRules: null,
766 setReviewers: null,
767 setReviewers: null,
767 controller: null,
768 controller: null,
768
769
769 setSelectors: function () {
770 setSelectors: function () {
770 var self = this;
771 var self = this;
771 self.editButton = $('#open_edit_reviewers');
772 self.editButton = $('#open_edit_reviewers');
772 self.closeButton =$('#close_edit_reviewers');
773 self.closeButton =$('#close_edit_reviewers');
773 self.addButton = $('#add_reviewer');
774 self.addButton = $('#add_reviewer');
774 self.removeButtons = $('.reviewer_member_remove,.reviewer_member_mandatory_remove');
775 self.removeButtons = $('.reviewer_member_remove,.reviewer_member_mandatory_remove');
775 },
776 },
776
777
777 init: function (controller, reviewRules, setReviewers) {
778 init: function (controller, reviewRules, setReviewers) {
778 var self = this;
779 var self = this;
779 self.setSelectors();
780 self.setSelectors();
780
781
781 self.controller = controller;
782 self.controller = controller;
782 self.reviewRules = reviewRules;
783 self.reviewRules = reviewRules;
783 self.setReviewers = setReviewers;
784 self.setReviewers = setReviewers;
784
785
785 self.editButton.on('click', function (e) {
786 self.editButton.on('click', function (e) {
786 self.edit();
787 self.edit();
787 });
788 });
788 self.closeButton.on('click', function (e) {
789 self.closeButton.on('click', function (e) {
789 self.close();
790 self.close();
790 self.renderReviewers();
791 self.renderReviewers();
791 });
792 });
792
793
793 self.renderReviewers();
794 self.renderReviewers();
794
795
795 },
796 },
796
797
797 renderReviewers: function () {
798 renderReviewers: function () {
798 var self = this;
799 var self = this;
799
800
800 if (self.setReviewers.reviewers === undefined) {
801 if (self.setReviewers.reviewers === undefined) {
801 return
802 return
802 }
803 }
803 if (self.setReviewers.reviewers.length === 0) {
804 if (self.setReviewers.reviewers.length === 0) {
804 self.controller.emptyReviewersTable('<tr id="reviewer-empty-msg"><td colspan="6">No reviewers</td></tr>');
805 self.controller.emptyReviewersTable('<tr id="reviewer-empty-msg"><td colspan="6">No reviewers</td></tr>');
805 return
806 return
806 }
807 }
807
808
808 self.controller.emptyReviewersTable();
809 self.controller.emptyReviewersTable();
809
810
810 $.each(self.setReviewers.reviewers, function (key, val) {
811 $.each(self.setReviewers.reviewers, function (key, val) {
811
812
812 var member = val;
813 var member = val;
813 if (member.role === self.controller.ROLE_REVIEWER) {
814 if (member.role === self.controller.ROLE_REVIEWER) {
814 var entry = renderTemplate('reviewMemberEntry', {
815 var entry = renderTemplate('reviewMemberEntry', {
815 'member': member,
816 'member': member,
816 'mandatory': member.mandatory,
817 'mandatory': member.mandatory,
817 'role': member.role,
818 'role': member.role,
818 'reasons': member.reasons,
819 'reasons': member.reasons,
819 'allowed_to_update': member.allowed_to_update,
820 'allowed_to_update': member.allowed_to_update,
820 'review_status': member.review_status,
821 'review_status': member.review_status,
821 'review_status_label': member.review_status_label,
822 'review_status_label': member.review_status_label,
822 'user_group': member.user_group,
823 'user_group': member.user_group,
823 'create': false
824 'create': false,
825 'rhodecode_user': templateContext.rhodecode_user
824 });
826 });
825
827
826 $(self.controller.$reviewMembers.selector).append(entry)
828 $(self.controller.$reviewMembers.selector).append(entry)
827 }
829 }
828 });
830 });
829
831
830 tooltipActivate();
832 tooltipActivate();
831 },
833 },
832
834
833 edit: function (event) {
835 edit: function (event) {
834 var self = this;
836 var self = this;
835 self.editButton.hide();
837 self.editButton.hide();
836 self.closeButton.show();
838 self.closeButton.show();
837 self.addButton.show();
839 self.addButton.show();
838 $(self.removeButtons.selector).css('visibility', 'visible');
840 $(self.removeButtons.selector).css('visibility', 'visible');
839 // review rules
841 // review rules
840 self.controller.loadReviewRules(this.reviewRules);
842 self.controller.loadReviewRules(this.reviewRules);
841 },
843 },
842
844
843 close: function (event) {
845 close: function (event) {
844 var self = this;
846 var self = this;
845 this.editButton.show();
847 this.editButton.show();
846 this.closeButton.hide();
848 this.closeButton.hide();
847 this.addButton.hide();
849 this.addButton.hide();
848 $(this.removeButtons.selector).css('visibility', 'hidden');
850 $(this.removeButtons.selector).css('visibility', 'hidden');
849 // hide review rules
851 // hide review rules
850 self.controller.hideReviewRules();
852 self.controller.hideReviewRules();
851 }
853 }
852 };
854 };
853
855
854 /**
856 /**
855 * Reviewer display panel
857 * Reviewer display panel
856 */
858 */
857 window.ObserversPanel = {
859 window.ObserversPanel = {
858 editButton: null,
860 editButton: null,
859 closeButton: null,
861 closeButton: null,
860 addButton: null,
862 addButton: null,
861 removeButtons: null,
863 removeButtons: null,
862 reviewRules: null,
864 reviewRules: null,
863 setReviewers: null,
865 setReviewers: null,
864 controller: null,
866 controller: null,
865
867
866 setSelectors: function () {
868 setSelectors: function () {
867 var self = this;
869 var self = this;
868 self.editButton = $('#open_edit_observers');
870 self.editButton = $('#open_edit_observers');
869 self.closeButton =$('#close_edit_observers');
871 self.closeButton =$('#close_edit_observers');
870 self.addButton = $('#add_observer');
872 self.addButton = $('#add_observer');
871 self.removeButtons = $('.observer_member_remove,.observer_member_mandatory_remove');
873 self.removeButtons = $('.observer_member_remove,.observer_member_mandatory_remove');
872 },
874 },
873
875
874 init: function (controller, reviewRules, setReviewers) {
876 init: function (controller, reviewRules, setReviewers) {
875 var self = this;
877 var self = this;
876 self.setSelectors();
878 self.setSelectors();
877
879
878 self.controller = controller;
880 self.controller = controller;
879 self.reviewRules = reviewRules;
881 self.reviewRules = reviewRules;
880 self.setReviewers = setReviewers;
882 self.setReviewers = setReviewers;
881
883
882 self.editButton.on('click', function (e) {
884 self.editButton.on('click', function (e) {
883 self.edit();
885 self.edit();
884 });
886 });
885 self.closeButton.on('click', function (e) {
887 self.closeButton.on('click', function (e) {
886 self.close();
888 self.close();
887 self.renderObservers();
889 self.renderObservers();
888 });
890 });
889
891
890 self.renderObservers();
892 self.renderObservers();
891
893
892 },
894 },
893
895
894 renderObservers: function () {
896 renderObservers: function () {
895 var self = this;
897 var self = this;
896 if (self.setReviewers.observers === undefined) {
898 if (self.setReviewers.observers === undefined) {
897 return
899 return
898 }
900 }
899 if (self.setReviewers.observers.length === 0) {
901 if (self.setReviewers.observers.length === 0) {
900 self.controller.emptyObserversTable('<tr id="observer-empty-msg"><td colspan="6">No observers</td></tr>');
902 self.controller.emptyObserversTable('<tr id="observer-empty-msg"><td colspan="6">No observers</td></tr>');
901 return
903 return
902 }
904 }
903
905
904 self.controller.emptyObserversTable();
906 self.controller.emptyObserversTable();
905
907
906 $.each(self.setReviewers.observers, function (key, val) {
908 $.each(self.setReviewers.observers, function (key, val) {
907 var member = val;
909 var member = val;
908 if (member.role === self.controller.ROLE_OBSERVER) {
910 if (member.role === self.controller.ROLE_OBSERVER) {
909 var entry = renderTemplate('reviewMemberEntry', {
911 var entry = renderTemplate('reviewMemberEntry', {
910 'member': member,
912 'member': member,
911 'mandatory': member.mandatory,
913 'mandatory': member.mandatory,
912 'role': member.role,
914 'role': member.role,
913 'reasons': member.reasons,
915 'reasons': member.reasons,
914 'allowed_to_update': member.allowed_to_update,
916 'allowed_to_update': member.allowed_to_update,
915 'review_status': member.review_status,
917 'review_status': member.review_status,
916 'review_status_label': member.review_status_label,
918 'review_status_label': member.review_status_label,
917 'user_group': member.user_group,
919 'user_group': member.user_group,
918 'create': false
920 'create': false,
921 'rhodecode_user': templateContext.rhodecode_user
919 });
922 });
920
923
921 $(self.controller.$observerMembers.selector).append(entry)
924 $(self.controller.$observerMembers.selector).append(entry)
922 }
925 }
923 });
926 });
924
927
925 tooltipActivate();
928 tooltipActivate();
926 },
929 },
927
930
928 edit: function (event) {
931 edit: function (event) {
929 this.editButton.hide();
932 this.editButton.hide();
930 this.closeButton.show();
933 this.closeButton.show();
931 this.addButton.show();
934 this.addButton.show();
932 $(this.removeButtons.selector).css('visibility', 'visible');
935 $(this.removeButtons.selector).css('visibility', 'visible');
933 },
936 },
934
937
935 close: function (event) {
938 close: function (event) {
936 this.editButton.show();
939 this.editButton.show();
937 this.closeButton.hide();
940 this.closeButton.hide();
938 this.addButton.hide();
941 this.addButton.hide();
939 $(this.removeButtons.selector).css('visibility', 'hidden');
942 $(this.removeButtons.selector).css('visibility', 'hidden');
940 }
943 }
941
944
942 };
945 };
943
946
944 window.PRDetails = {
947 window.PRDetails = {
945 editButton: null,
948 editButton: null,
946 closeButton: null,
949 closeButton: null,
947 deleteButton: null,
950 deleteButton: null,
948 viewFields: null,
951 viewFields: null,
949 editFields: null,
952 editFields: null,
950
953
951 setSelectors: function () {
954 setSelectors: function () {
952 var self = this;
955 var self = this;
953 self.editButton = $('#open_edit_pullrequest')
956 self.editButton = $('#open_edit_pullrequest')
954 self.closeButton = $('#close_edit_pullrequest')
957 self.closeButton = $('#close_edit_pullrequest')
955 self.deleteButton = $('#delete_pullrequest')
958 self.deleteButton = $('#delete_pullrequest')
956 self.viewFields = $('#pr-desc, #pr-title')
959 self.viewFields = $('#pr-desc, #pr-title')
957 self.editFields = $('#pr-desc-edit, #pr-title-edit, .pr-save')
960 self.editFields = $('#pr-desc-edit, #pr-title-edit, .pr-save')
958 },
961 },
959
962
960 init: function () {
963 init: function () {
961 var self = this;
964 var self = this;
962 self.setSelectors();
965 self.setSelectors();
963 self.editButton.on('click', function (e) {
966 self.editButton.on('click', function (e) {
964 self.edit();
967 self.edit();
965 });
968 });
966 self.closeButton.on('click', function (e) {
969 self.closeButton.on('click', function (e) {
967 self.view();
970 self.view();
968 });
971 });
969 },
972 },
970
973
971 edit: function (event) {
974 edit: function (event) {
972 var cmInstance = $('#pr-description-input').get(0).MarkupForm.cm;
975 var cmInstance = $('#pr-description-input').get(0).MarkupForm.cm;
973 this.viewFields.hide();
976 this.viewFields.hide();
974 this.editButton.hide();
977 this.editButton.hide();
975 this.deleteButton.hide();
978 this.deleteButton.hide();
976 this.closeButton.show();
979 this.closeButton.show();
977 this.editFields.show();
980 this.editFields.show();
978 cmInstance.refresh();
981 cmInstance.refresh();
979 },
982 },
980
983
981 view: function (event) {
984 view: function (event) {
982 this.editButton.show();
985 this.editButton.show();
983 this.deleteButton.show();
986 this.deleteButton.show();
984 this.editFields.hide();
987 this.editFields.hide();
985 this.closeButton.hide();
988 this.closeButton.hide();
986 this.viewFields.show();
989 this.viewFields.show();
987 }
990 }
988 };
991 };
989
992
990 /**
993 /**
991 * OnLine presence using channelstream
994 * OnLine presence using channelstream
992 */
995 */
993 window.ReviewerPresenceController = function (channel) {
996 window.ReviewerPresenceController = function (channel) {
994 var self = this;
997 var self = this;
995 this.channel = channel;
998 this.channel = channel;
996 this.users = {};
999 this.users = {};
997
1000
998 this.storeUsers = function (users) {
1001 this.storeUsers = function (users) {
999 self.users = {}
1002 self.users = {}
1000 $.each(users, function (index, value) {
1003 $.each(users, function (index, value) {
1001 var userId = value.state.id;
1004 var userId = value.state.id;
1002 self.users[userId] = value.state;
1005 self.users[userId] = value.state;
1003 })
1006 })
1004 }
1007 }
1005
1008
1006 this.render = function () {
1009 this.render = function () {
1007 $.each($('.reviewer_entry'), function (index, value) {
1010 $.each($('.reviewer_entry'), function (index, value) {
1008 var userData = $(value).data();
1011 var userData = $(value).data();
1009 if (self.users[userData.reviewerUserId] !== undefined) {
1012 if (self.users[userData.reviewerUserId] !== undefined) {
1010 $(value).find('.presence-state').show();
1013 $(value).find('.presence-state').show();
1011 } else {
1014 } else {
1012 $(value).find('.presence-state').hide();
1015 $(value).find('.presence-state').hide();
1013 }
1016 }
1014 })
1017 })
1015 };
1018 };
1016
1019
1017 this.handlePresence = function (data) {
1020 this.handlePresence = function (data) {
1018 if (data.type == 'presence' && data.channel === self.channel) {
1021 if (data.type == 'presence' && data.channel === self.channel) {
1019 this.storeUsers(data.users);
1022 this.storeUsers(data.users);
1020 this.render();
1023 this.render();
1021 }
1024 }
1022 };
1025 };
1023
1026
1024 this.handleChannelUpdate = function (data) {
1027 this.handleChannelUpdate = function (data) {
1025 if (data.channel === this.channel) {
1028 if (data.channel === this.channel) {
1026 this.storeUsers(data.state.users);
1029 this.storeUsers(data.state.users);
1027 this.render();
1030 this.render();
1028 }
1031 }
1029
1032
1030 };
1033 };
1031
1034
1032 /* subscribe to the current presence */
1035 /* subscribe to the current presence */
1033 $.Topic('/connection_controller/presence').subscribe(this.handlePresence.bind(this));
1036 $.Topic('/connection_controller/presence').subscribe(this.handlePresence.bind(this));
1034 /* subscribe to updates e.g connect/disconnect */
1037 /* subscribe to updates e.g connect/disconnect */
1035 $.Topic('/connection_controller/channel_update').subscribe(this.handleChannelUpdate.bind(this));
1038 $.Topic('/connection_controller/channel_update').subscribe(this.handleChannelUpdate.bind(this));
1036
1039
1037 };
1040 };
1038
1041
1039 window.refreshCommentsSuccess = function(targetNode, counterNode, extraCallback) {
1042 window.refreshCommentsSuccess = function(targetNode, counterNode, extraCallback) {
1040 var $targetElem = targetNode;
1043 var $targetElem = targetNode;
1041 var $counterElem = counterNode;
1044 var $counterElem = counterNode;
1042
1045
1043 return function (data) {
1046 return function (data) {
1044 var newCount = $(data).data('counter');
1047 var newCount = $(data).data('counter');
1045 if (newCount !== undefined) {
1048 if (newCount !== undefined) {
1046 var callback = function () {
1049 var callback = function () {
1047 $counterElem.animate({'opacity': 1.00}, 200)
1050 $counterElem.animate({'opacity': 1.00}, 200)
1048 $counterElem.html(newCount);
1051 $counterElem.html(newCount);
1049 };
1052 };
1050 $counterElem.animate({'opacity': 0.15}, 200, callback);
1053 $counterElem.animate({'opacity': 0.15}, 200, callback);
1051 }
1054 }
1052
1055
1053 $targetElem.css('opacity', 1);
1056 $targetElem.css('opacity', 1);
1054 $targetElem.html(data);
1057 $targetElem.html(data);
1055 tooltipActivate();
1058 tooltipActivate();
1056
1059
1057 if (extraCallback !== undefined) {
1060 if (extraCallback !== undefined) {
1058 extraCallback(data)
1061 extraCallback(data)
1059 }
1062 }
1060 }
1063 }
1061 }
1064 }
1062
1065
1063 window.refreshComments = function (version) {
1066 window.refreshComments = function (version) {
1064 version = version || templateContext.pull_request_data.pull_request_version || '';
1067 version = version || templateContext.pull_request_data.pull_request_version || '';
1065
1068
1066 // Pull request case
1069 // Pull request case
1067 if (templateContext.pull_request_data.pull_request_id !== null) {
1070 if (templateContext.pull_request_data.pull_request_id !== null) {
1068 var params = {
1071 var params = {
1069 'pull_request_id': templateContext.pull_request_data.pull_request_id,
1072 'pull_request_id': templateContext.pull_request_data.pull_request_id,
1070 'repo_name': templateContext.repo_name,
1073 'repo_name': templateContext.repo_name,
1071 'version': version,
1074 'version': version,
1072 };
1075 };
1073 var loadUrl = pyroutes.url('pullrequest_comments', params);
1076 var loadUrl = pyroutes.url('pullrequest_comments', params);
1074 } // commit case
1077 } // commit case
1075 else {
1078 else {
1076 return
1079 return
1077 }
1080 }
1078
1081
1079 var currentIDs = []
1082 var currentIDs = []
1080 $.each($('.comment'), function (idx, element) {
1083 $.each($('.comment'), function (idx, element) {
1081 currentIDs.push($(element).data('commentId'));
1084 currentIDs.push($(element).data('commentId'));
1082 });
1085 });
1083 var data = {"comments": currentIDs};
1086 var data = {"comments": currentIDs};
1084
1087
1085 var $targetElem = $('.comments-content-table');
1088 var $targetElem = $('.comments-content-table');
1086 $targetElem.css('opacity', 0.3);
1089 $targetElem.css('opacity', 0.3);
1087 var $counterElem = $('#comments-count');
1090 var $counterElem = $('#comments-count');
1088 var success = refreshCommentsSuccess($targetElem, $counterElem);
1091 var success = refreshCommentsSuccess($targetElem, $counterElem);
1089 ajaxPOST(loadUrl, data, success, null, {})
1092 ajaxPOST(loadUrl, data, success, null, {})
1090
1093
1091 }
1094 }
1092
1095
1093 window.refreshTODOs = function (version) {
1096 window.refreshTODOs = function (version) {
1094 version = version || templateContext.pull_request_data.pull_request_version || '';
1097 version = version || templateContext.pull_request_data.pull_request_version || '';
1095 // Pull request case
1098 // Pull request case
1096 if (templateContext.pull_request_data.pull_request_id !== null) {
1099 if (templateContext.pull_request_data.pull_request_id !== null) {
1097 var params = {
1100 var params = {
1098 'pull_request_id': templateContext.pull_request_data.pull_request_id,
1101 'pull_request_id': templateContext.pull_request_data.pull_request_id,
1099 'repo_name': templateContext.repo_name,
1102 'repo_name': templateContext.repo_name,
1100 'version': version,
1103 'version': version,
1101 };
1104 };
1102 var loadUrl = pyroutes.url('pullrequest_todos', params);
1105 var loadUrl = pyroutes.url('pullrequest_todos', params);
1103 } // commit case
1106 } // commit case
1104 else {
1107 else {
1105 return
1108 return
1106 }
1109 }
1107
1110
1108 var currentIDs = []
1111 var currentIDs = []
1109 $.each($('.comment'), function (idx, element) {
1112 $.each($('.comment'), function (idx, element) {
1110 currentIDs.push($(element).data('commentId'));
1113 currentIDs.push($(element).data('commentId'));
1111 });
1114 });
1112
1115
1113 var data = {"comments": currentIDs};
1116 var data = {"comments": currentIDs};
1114 var $targetElem = $('.todos-content-table');
1117 var $targetElem = $('.todos-content-table');
1115 $targetElem.css('opacity', 0.3);
1118 $targetElem.css('opacity', 0.3);
1116 var $counterElem = $('#todos-count');
1119 var $counterElem = $('#todos-count');
1117 var success = refreshCommentsSuccess($targetElem, $counterElem);
1120 var success = refreshCommentsSuccess($targetElem, $counterElem);
1118
1121
1119 ajaxPOST(loadUrl, data, success, null, {})
1122 ajaxPOST(loadUrl, data, success, null, {})
1120
1123
1121 }
1124 }
1122
1125
1123 window.refreshDraftComments = function () {
1126 window.refreshDraftComments = function () {
1124
1127
1125 // Pull request case
1128 // Pull request case
1126 if (templateContext.pull_request_data.pull_request_id !== null) {
1129 if (templateContext.pull_request_data.pull_request_id !== null) {
1127 var params = {
1130 var params = {
1128 'pull_request_id': templateContext.pull_request_data.pull_request_id,
1131 'pull_request_id': templateContext.pull_request_data.pull_request_id,
1129 'repo_name': templateContext.repo_name,
1132 'repo_name': templateContext.repo_name,
1130 };
1133 };
1131 var loadUrl = pyroutes.url('pullrequest_drafts', params);
1134 var loadUrl = pyroutes.url('pullrequest_drafts', params);
1132 } // commit case
1135 } // commit case
1133 else {
1136 else {
1134 return
1137 return
1135 }
1138 }
1136
1139
1137 var data = {};
1140 var data = {};
1138
1141
1139 var $targetElem = $('.drafts-content-table');
1142 var $targetElem = $('.drafts-content-table');
1140 $targetElem.css('opacity', 0.3);
1143 $targetElem.css('opacity', 0.3);
1141 var $counterElem = $('#drafts-count');
1144 var $counterElem = $('#drafts-count');
1142 var extraCallback = function(data) {
1145 var extraCallback = function(data) {
1143 if ($(data).data('counter') == 0){
1146 if ($(data).data('counter') == 0){
1144 $('#draftsTable').hide();
1147 $('#draftsTable').hide();
1145 } else {
1148 } else {
1146 $('#draftsTable').show();
1149 $('#draftsTable').show();
1147 }
1150 }
1148 // uncheck on load the select all checkbox
1151 // uncheck on load the select all checkbox
1149 $('[name=select_all_drafts]').prop('checked', 0);
1152 $('[name=select_all_drafts]').prop('checked', 0);
1150 }
1153 }
1151 var success = refreshCommentsSuccess($targetElem, $counterElem, extraCallback);
1154 var success = refreshCommentsSuccess($targetElem, $counterElem, extraCallback);
1152
1155
1153 ajaxPOST(loadUrl, data, success, null, {})
1156 ajaxPOST(loadUrl, data, success, null, {})
1154 };
1157 };
1155
1158
1156 window.refreshAllComments = function (version) {
1159 window.refreshAllComments = function (version) {
1157 version = version || templateContext.pull_request_data.pull_request_version || '';
1160 version = version || templateContext.pull_request_data.pull_request_version || '';
1158
1161
1159 refreshComments(version);
1162 refreshComments(version);
1160 refreshTODOs(version);
1163 refreshTODOs(version);
1161 };
1164 };
1162
1165
1163 window.sidebarComment = function (commentId) {
1166 window.sidebarComment = function (commentId) {
1164 var jsonData = $('#commentHovercard{0}'.format(commentId)).data('commentJsonB64');
1167 var jsonData = $('#commentHovercard{0}'.format(commentId)).data('commentJsonB64');
1165 if (!jsonData) {
1168 if (!jsonData) {
1166 return 'Failed to load comment {0}'.format(commentId)
1169 return 'Failed to load comment {0}'.format(commentId)
1167 }
1170 }
1168 var funcData = JSON.parse(atob(jsonData));
1171 var funcData = JSON.parse(atob(jsonData));
1169 return renderTemplate('sideBarCommentHovercard', funcData)
1172 return renderTemplate('sideBarCommentHovercard', funcData)
1170 };
1173 };
@@ -1,1395 +1,1404 b''
1 <%namespace name="base" file="/base/base.mako"/>
1 <%namespace name="base" file="/base/base.mako"/>
2 <%namespace name="commentblock" file="/changeset/changeset_file_comment.mako"/>
2 <%namespace name="commentblock" file="/changeset/changeset_file_comment.mako"/>
3
3
4 <%def name="diff_line_anchor(commit, filename, line, type)"><%
4 <%def name="diff_line_anchor(commit, filename, line, type)"><%
5 return '%s_%s_%i' % (h.md5_safe(commit+filename), type, line)
5 return '%s_%s_%i' % (h.md5_safe(commit+filename), type, line)
6 %></%def>
6 %></%def>
7
7
8 <%def name="action_class(action)">
8 <%def name="action_class(action)">
9 <%
9 <%
10 return {
10 return {
11 '-': 'cb-deletion',
11 '-': 'cb-deletion',
12 '+': 'cb-addition',
12 '+': 'cb-addition',
13 ' ': 'cb-context',
13 ' ': 'cb-context',
14 }.get(action, 'cb-empty')
14 }.get(action, 'cb-empty')
15 %>
15 %>
16 </%def>
16 </%def>
17
17
18 <%def name="op_class(op_id)">
18 <%def name="op_class(op_id)">
19 <%
19 <%
20 return {
20 return {
21 DEL_FILENODE: 'deletion', # file deleted
21 DEL_FILENODE: 'deletion', # file deleted
22 BIN_FILENODE: 'warning' # binary diff hidden
22 BIN_FILENODE: 'warning' # binary diff hidden
23 }.get(op_id, 'addition')
23 }.get(op_id, 'addition')
24 %>
24 %>
25 </%def>
25 </%def>
26
26
27
27
28
28
29 <%def name="render_diffset(diffset, commit=None,
29 <%def name="render_diffset(diffset, commit=None,
30
30
31 # collapse all file diff entries when there are more than this amount of files in the diff
31 # collapse all file diff entries when there are more than this amount of files in the diff
32 collapse_when_files_over=20,
32 collapse_when_files_over=20,
33
33
34 # collapse lines in the diff when more than this amount of lines changed in the file diff
34 # collapse lines in the diff when more than this amount of lines changed in the file diff
35 lines_changed_limit=500,
35 lines_changed_limit=500,
36
36
37 # add a ruler at to the output
37 # add a ruler at to the output
38 ruler_at_chars=0,
38 ruler_at_chars=0,
39
39
40 # show inline comments
40 # show inline comments
41 use_comments=False,
41 use_comments=False,
42
42
43 # disable new comments
43 # disable new comments
44 disable_new_comments=False,
44 disable_new_comments=False,
45
45
46 # special file-comments that were deleted in previous versions
46 # special file-comments that were deleted in previous versions
47 # it's used for showing outdated comments for deleted files in a PR
47 # it's used for showing outdated comments for deleted files in a PR
48 deleted_files_comments=None,
48 deleted_files_comments=None,
49
49
50 # for cache purpose
50 # for cache purpose
51 inline_comments=None,
51 inline_comments=None,
52
52
53 # additional menu for PRs
53 # additional menu for PRs
54 pull_request_menu=None,
54 pull_request_menu=None,
55
55
56 # show/hide todo next to comments
56 # show/hide todo next to comments
57 show_todos=True,
57 show_todos=True,
58
58
59 )">
59 )">
60
60
61 <%
61 <%
62 diffset_container_id = h.md5(diffset.target_ref)
62 diffset_container_id = h.md5(diffset.target_ref)
63 collapse_all = len(diffset.files) > collapse_when_files_over
63 collapse_all = len(diffset.files) > collapse_when_files_over
64 active_pattern_entries = h.get_active_pattern_entries(getattr(c, 'repo_name', None))
64 active_pattern_entries = h.get_active_pattern_entries(getattr(c, 'repo_name', None))
65 from rhodecode.lib.diffs import NEW_FILENODE, DEL_FILENODE, \
65 from rhodecode.lib.diffs import NEW_FILENODE, DEL_FILENODE, \
66 MOD_FILENODE, RENAMED_FILENODE, CHMOD_FILENODE, BIN_FILENODE, COPIED_FILENODE
66 MOD_FILENODE, RENAMED_FILENODE, CHMOD_FILENODE, BIN_FILENODE, COPIED_FILENODE
67 %>
67 %>
68
68
69 %if use_comments:
69 %if use_comments:
70
70
71 ## Template for injecting comments
71 ## Template for injecting comments
72 <div id="cb-comments-inline-container-template" class="js-template">
72 <div id="cb-comments-inline-container-template" class="js-template">
73 ${inline_comments_container([])}
73 ${inline_comments_container([])}
74 </div>
74 </div>
75
75
76 <div class="js-template" id="cb-comment-inline-form-template">
76 <div class="js-template" id="cb-comment-inline-form-template">
77 <div class="comment-inline-form ac">
77 <div class="comment-inline-form ac">
78 %if not c.rhodecode_user.is_default:
78 %if not c.rhodecode_user.is_default:
79 ## render template for inline comments
79 ## render template for inline comments
80 ${commentblock.comment_form(form_type='inline')}
80 ${commentblock.comment_form(form_type='inline')}
81 %endif
81 %endif
82 </div>
82 </div>
83 </div>
83 </div>
84
84
85 %endif
85 %endif
86
86
87 %if c.user_session_attrs["diffmode"] == 'sideside':
87 %if c.user_session_attrs["diffmode"] == 'sideside':
88 <style>
88 <style>
89 .wrapper {
89 .wrapper {
90 max-width: 1600px !important;
90 max-width: 1600px !important;
91 }
91 }
92 </style>
92 </style>
93 %endif
93 %endif
94
94
95 %if ruler_at_chars:
95 %if ruler_at_chars:
96 <style>
96 <style>
97 .diff table.cb .cb-content:after {
97 .diff table.cb .cb-content:after {
98 content: "";
98 content: "";
99 border-left: 1px solid blue;
99 border-left: 1px solid blue;
100 position: absolute;
100 position: absolute;
101 top: 0;
101 top: 0;
102 height: 18px;
102 height: 18px;
103 opacity: .2;
103 opacity: .2;
104 z-index: 10;
104 z-index: 10;
105 //## +5 to account for diff action (+/-)
105 //## +5 to account for diff action (+/-)
106 left: ${ruler_at_chars + 5}ch;
106 left: ${ruler_at_chars + 5}ch;
107 </style>
107 </style>
108 %endif
108 %endif
109
109
110 <div class="diffset ${disable_new_comments and 'diffset-comments-disabled'}">
110 <div class="diffset ${disable_new_comments and 'diffset-comments-disabled'}">
111
111
112 <div style="height: 20px; line-height: 20px">
112 <div style="height: 20px; line-height: 20px">
113 ## expand/collapse action
113 ## expand/collapse action
114 <div class="pull-left">
114 <div class="pull-left">
115 <a class="${'collapsed' if collapse_all else ''}" href="#expand-files" onclick="toggleExpand(this, '${diffset_container_id}'); return false">
115 <a class="${'collapsed' if collapse_all else ''}" href="#expand-files" onclick="toggleExpand(this, '${diffset_container_id}'); return false">
116 % if collapse_all:
116 % if collapse_all:
117 <i class="icon-plus-squared-alt icon-no-margin"></i>${_('Expand all files')}
117 <i class="icon-plus-squared-alt icon-no-margin"></i>${_('Expand all files')}
118 % else:
118 % else:
119 <i class="icon-minus-squared-alt icon-no-margin"></i>${_('Collapse all files')}
119 <i class="icon-minus-squared-alt icon-no-margin"></i>${_('Collapse all files')}
120 % endif
120 % endif
121 </a>
121 </a>
122
122
123 </div>
123 </div>
124
124
125 ## todos
125 ## todos
126 % if show_todos and getattr(c, 'at_version', None):
126 % if show_todos and getattr(c, 'at_version', None):
127 <div class="pull-right">
127 <div class="pull-right">
128 <i class="icon-flag-filled" style="color: #949494">TODOs:</i>
128 <i class="icon-flag-filled" style="color: #949494">TODOs:</i>
129 ${_('not available in this view')}
129 ${_('not available in this view')}
130 </div>
130 </div>
131 % elif show_todos:
131 % elif show_todos:
132 <div class="pull-right">
132 <div class="pull-right">
133 <div class="comments-number" style="padding-left: 10px">
133 <div class="comments-number" style="padding-left: 10px">
134 % if hasattr(c, 'unresolved_comments') and hasattr(c, 'resolved_comments'):
134 % if hasattr(c, 'unresolved_comments') and hasattr(c, 'resolved_comments'):
135 <i class="icon-flag-filled" style="color: #949494">TODOs:</i>
135 <i class="icon-flag-filled" style="color: #949494">TODOs:</i>
136 % if c.unresolved_comments:
136 % if c.unresolved_comments:
137 <a href="#show-todos" onclick="$('#todo-box').toggle(); return false">
137 <a href="#show-todos" onclick="$('#todo-box').toggle(); return false">
138 ${_('{} unresolved').format(len(c.unresolved_comments))}
138 ${_('{} unresolved').format(len(c.unresolved_comments))}
139 </a>
139 </a>
140 % else:
140 % else:
141 ${_('0 unresolved')}
141 ${_('0 unresolved')}
142 % endif
142 % endif
143
143
144 ${_('{} Resolved').format(len(c.resolved_comments))}
144 ${_('{} Resolved').format(len(c.resolved_comments))}
145 % endif
145 % endif
146 </div>
146 </div>
147 </div>
147 </div>
148 % endif
148 % endif
149
149
150 ## ## comments
150 ## ## comments
151 ## <div class="pull-right">
151 ## <div class="pull-right">
152 ## <div class="comments-number" style="padding-left: 10px">
152 ## <div class="comments-number" style="padding-left: 10px">
153 ## % if hasattr(c, 'comments') and hasattr(c, 'inline_cnt'):
153 ## % if hasattr(c, 'comments') and hasattr(c, 'inline_cnt'):
154 ## <i class="icon-comment" style="color: #949494">COMMENTS:</i>
154 ## <i class="icon-comment" style="color: #949494">COMMENTS:</i>
155 ## % if c.comments:
155 ## % if c.comments:
156 ## <a href="#comments">${_ungettext("{} General", "{} General", len(c.comments)).format(len(c.comments))}</a>,
156 ## <a href="#comments">${_ungettext("{} General", "{} General", len(c.comments)).format(len(c.comments))}</a>,
157 ## % else:
157 ## % else:
158 ## ${_('0 General')}
158 ## ${_('0 General')}
159 ## % endif
159 ## % endif
160 ##
160 ##
161 ## % if c.inline_cnt:
161 ## % if c.inline_cnt:
162 ## <a href="#" onclick="return Rhodecode.comments.nextComment();"
162 ## <a href="#" onclick="return Rhodecode.comments.nextComment();"
163 ## id="inline-comments-counter">${_ungettext("{} Inline", "{} Inline", c.inline_cnt).format(c.inline_cnt)}
163 ## id="inline-comments-counter">${_ungettext("{} Inline", "{} Inline", c.inline_cnt).format(c.inline_cnt)}
164 ## </a>
164 ## </a>
165 ## % else:
165 ## % else:
166 ## ${_('0 Inline')}
166 ## ${_('0 Inline')}
167 ## % endif
167 ## % endif
168 ## % endif
168 ## % endif
169 ##
169 ##
170 ## % if pull_request_menu:
170 ## % if pull_request_menu:
171 ## <%
171 ## <%
172 ## outdated_comm_count_ver = pull_request_menu['outdated_comm_count_ver']
172 ## outdated_comm_count_ver = pull_request_menu['outdated_comm_count_ver']
173 ## %>
173 ## %>
174 ##
174 ##
175 ## % if outdated_comm_count_ver:
175 ## % if outdated_comm_count_ver:
176 ## <a href="#" onclick="showOutdated(); Rhodecode.comments.nextOutdatedComment(); return false;">
176 ## <a href="#" onclick="showOutdated(); Rhodecode.comments.nextOutdatedComment(); return false;">
177 ## (${_("{} Outdated").format(outdated_comm_count_ver)})
177 ## (${_("{} Outdated").format(outdated_comm_count_ver)})
178 ## </a>
178 ## </a>
179 ## <a href="#" class="showOutdatedComments" onclick="showOutdated(this); return false;"> | ${_('show outdated')}</a>
179 ## <a href="#" class="showOutdatedComments" onclick="showOutdated(this); return false;"> | ${_('show outdated')}</a>
180 ## <a href="#" class="hideOutdatedComments" style="display: none" onclick="hideOutdated(this); return false;"> | ${_('hide outdated')}</a>
180 ## <a href="#" class="hideOutdatedComments" style="display: none" onclick="hideOutdated(this); return false;"> | ${_('hide outdated')}</a>
181 ## % else:
181 ## % else:
182 ## (${_("{} Outdated").format(outdated_comm_count_ver)})
182 ## (${_("{} Outdated").format(outdated_comm_count_ver)})
183 ## % endif
183 ## % endif
184 ##
184 ##
185 ## % endif
185 ## % endif
186 ##
186 ##
187 ## </div>
187 ## </div>
188 ## </div>
188 ## </div>
189
189
190 </div>
190 </div>
191
191
192 % if diffset.limited_diff:
192 % if diffset.limited_diff:
193 <div class="diffset-heading ${(diffset.limited_diff and 'diffset-heading-warning' or '')}">
193 <div class="diffset-heading ${(diffset.limited_diff and 'diffset-heading-warning' or '')}">
194 <h2 class="clearinner">
194 <h2 class="clearinner">
195 ${_('The requested changes are too big and content was truncated.')}
195 ${_('The requested changes are too big and content was truncated.')}
196 <a href="${h.current_route_path(request, fulldiff=1)}" onclick="return confirm('${_("Showing a big diff might take some time and resources, continue?")}')">${_('Show full diff')}</a>
196 <a href="${h.current_route_path(request, fulldiff=1)}" onclick="return confirm('${_("Showing a big diff might take some time and resources, continue?")}')">${_('Show full diff')}</a>
197 </h2>
197 </h2>
198 </div>
198 </div>
199 % endif
199 % endif
200
200
201 <div id="todo-box">
201 <div id="todo-box">
202 % if hasattr(c, 'unresolved_comments') and c.unresolved_comments:
202 % if hasattr(c, 'unresolved_comments') and c.unresolved_comments:
203 % for co in c.unresolved_comments:
203 % for co in c.unresolved_comments:
204 <a class="permalink" href="#comment-${co.comment_id}"
204 <a class="permalink" href="#comment-${co.comment_id}"
205 onclick="Rhodecode.comments.scrollToComment($('#comment-${co.comment_id}'))">
205 onclick="Rhodecode.comments.scrollToComment($('#comment-${co.comment_id}'))">
206 <i class="icon-flag-filled-red"></i>
206 <i class="icon-flag-filled-red"></i>
207 ${co.comment_id}</a>${('' if loop.last else ',')}
207 ${co.comment_id}</a>${('' if loop.last else ',')}
208 % endfor
208 % endfor
209 % endif
209 % endif
210 </div>
210 </div>
211 %if diffset.has_hidden_changes:
211 %if diffset.has_hidden_changes:
212 <p class="empty_data">${_('Some changes may be hidden')}</p>
212 <p class="empty_data">${_('Some changes may be hidden')}</p>
213 %elif not diffset.files:
213 %elif not diffset.files:
214 <p class="empty_data">${_('No files')}</p>
214 <p class="empty_data">${_('No files')}</p>
215 %endif
215 %endif
216
216
217 <div class="filediffs">
217 <div class="filediffs">
218
218
219 ## initial value could be marked as False later on
219 ## initial value could be marked as False later on
220 <% over_lines_changed_limit = False %>
220 <% over_lines_changed_limit = False %>
221 %for i, filediff in enumerate(diffset.files):
221 %for i, filediff in enumerate(diffset.files):
222
222
223 %if filediff.source_file_path and filediff.target_file_path:
223 %if filediff.source_file_path and filediff.target_file_path:
224 %if filediff.source_file_path != filediff.target_file_path:
224 %if filediff.source_file_path != filediff.target_file_path:
225 ## file was renamed, or copied
225 ## file was renamed, or copied
226 %if RENAMED_FILENODE in filediff.patch['stats']['ops']:
226 %if RENAMED_FILENODE in filediff.patch['stats']['ops']:
227 <%
227 <%
228 final_file_name = h.literal(u'{} <i class="icon-angle-left"></i> <del>{}</del>'.format(filediff.target_file_path, filediff.source_file_path))
228 final_file_name = h.literal(u'{} <i class="icon-angle-left"></i> <del>{}</del>'.format(filediff.target_file_path, filediff.source_file_path))
229 final_path = filediff.target_file_path
229 final_path = filediff.target_file_path
230 %>
230 %>
231 %elif COPIED_FILENODE in filediff.patch['stats']['ops']:
231 %elif COPIED_FILENODE in filediff.patch['stats']['ops']:
232 <%
232 <%
233 final_file_name = h.literal(u'{} <i class="icon-angle-left"></i> {}'.format(filediff.target_file_path, filediff.source_file_path))
233 final_file_name = h.literal(u'{} <i class="icon-angle-left"></i> {}'.format(filediff.target_file_path, filediff.source_file_path))
234 final_path = filediff.target_file_path
234 final_path = filediff.target_file_path
235 %>
235 %>
236 %endif
236 %endif
237 %else:
237 %else:
238 ## file was modified
238 ## file was modified
239 <%
239 <%
240 final_file_name = filediff.source_file_path
240 final_file_name = filediff.source_file_path
241 final_path = final_file_name
241 final_path = final_file_name
242 %>
242 %>
243 %endif
243 %endif
244 %else:
244 %else:
245 %if filediff.source_file_path:
245 %if filediff.source_file_path:
246 ## file was deleted
246 ## file was deleted
247 <%
247 <%
248 final_file_name = filediff.source_file_path
248 final_file_name = filediff.source_file_path
249 final_path = final_file_name
249 final_path = final_file_name
250 %>
250 %>
251 %else:
251 %else:
252 ## file was added
252 ## file was added
253 <%
253 <%
254 final_file_name = filediff.target_file_path
254 final_file_name = filediff.target_file_path
255 final_path = final_file_name
255 final_path = final_file_name
256 %>
256 %>
257 %endif
257 %endif
258 %endif
258 %endif
259
259
260 <%
260 <%
261 lines_changed = filediff.patch['stats']['added'] + filediff.patch['stats']['deleted']
261 lines_changed = filediff.patch['stats']['added'] + filediff.patch['stats']['deleted']
262 over_lines_changed_limit = lines_changed > lines_changed_limit
262 over_lines_changed_limit = lines_changed > lines_changed_limit
263 %>
263 %>
264 ## anchor with support of sticky header
264 ## anchor with support of sticky header
265 <div class="anchor" id="a_${h.FID(filediff.raw_id, filediff.patch['filename'])}"></div>
265 <div class="anchor" id="a_${h.FID(filediff.raw_id, filediff.patch['filename'])}"></div>
266
266
267 <input ${(collapse_all and 'checked' or '')} class="filediff-collapse-state collapse-${diffset_container_id}" id="filediff-collapse-${id(filediff)}" type="checkbox" onchange="updateSticky();">
267 <input ${(collapse_all and 'checked' or '')} class="filediff-collapse-state collapse-${diffset_container_id}" id="filediff-collapse-${id(filediff)}" type="checkbox" onchange="updateSticky();">
268 <div
268 <div
269 class="filediff"
269 class="filediff"
270 data-f-path="${filediff.patch['filename']}"
270 data-f-path="${filediff.patch['filename']}"
271 data-anchor-id="${h.FID(filediff.raw_id, filediff.patch['filename'])}"
271 data-anchor-id="${h.FID(filediff.raw_id, filediff.patch['filename'])}"
272 >
272 >
273 <label for="filediff-collapse-${id(filediff)}" class="filediff-heading">
273 <label for="filediff-collapse-${id(filediff)}" class="filediff-heading">
274 <%
274 <%
275 file_comments = (get_inline_comments(inline_comments, filediff.patch['filename']) or {}).values()
275 file_comments = (get_inline_comments(inline_comments, filediff.patch['filename']) or {}).values()
276 total_file_comments = [_c for _c in h.itertools.chain.from_iterable(file_comments) if not (_c.outdated or _c.draft)]
276 total_file_comments = [_c for _c in h.itertools.chain.from_iterable(file_comments) if not (_c.outdated or _c.draft)]
277 %>
277 %>
278 <div class="filediff-collapse-indicator icon-"></div>
278 <div class="filediff-collapse-indicator icon-"></div>
279
279
280 ## Comments/Options PILL
280 ## Comments/Options PILL
281 <span class="pill-group pull-right">
281 <span class="pill-group pull-right">
282 <span class="pill" op="comments">
282 <span class="pill" op="comments">
283 <i class="icon-comment"></i> ${len(total_file_comments)}
283 <i class="icon-comment"></i> ${len(total_file_comments)}
284 </span>
284 </span>
285
285
286 <details class="details-reset details-inline-block">
286 <details class="details-reset details-inline-block">
287 <summary class="noselect">
287 <summary class="noselect">
288 <i class="pill icon-options cursor-pointer" op="options"></i>
288 <i class="pill icon-options cursor-pointer" op="options"></i>
289 </summary>
289 </summary>
290 <details-menu class="details-dropdown">
290 <details-menu class="details-dropdown">
291
291
292 <div class="dropdown-item">
292 <div class="dropdown-item">
293 <span>${final_path}</span>
293 <span>${final_path}</span>
294 <span class="pull-right icon-clipboard clipboard-action" data-clipboard-text="${final_path}" title="Copy file path"></span>
294 <span class="pull-right icon-clipboard clipboard-action" data-clipboard-text="${final_path}" title="Copy file path"></span>
295 </div>
295 </div>
296
296
297 <div class="dropdown-divider"></div>
297 <div class="dropdown-divider"></div>
298
298
299 <div class="dropdown-item">
299 <div class="dropdown-item">
300 <% permalink = request.current_route_url(_anchor='a_{}'.format(h.FID(filediff.raw_id, filediff.patch['filename']))) %>
300 <% permalink = request.current_route_url(_anchor='a_{}'.format(h.FID(filediff.raw_id, filediff.patch['filename']))) %>
301 <a href="${permalink}">¶ permalink</a>
301 <a href="${permalink}">¶ permalink</a>
302 <span class="pull-right icon-clipboard clipboard-action" data-clipboard-text="${permalink}" title="Copy permalink"></span>
302 <span class="pull-right icon-clipboard clipboard-action" data-clipboard-text="${permalink}" title="Copy permalink"></span>
303 </div>
303 </div>
304
304
305
305
306 </details-menu>
306 </details-menu>
307 </details>
307 </details>
308
308
309 </span>
309 </span>
310
310
311 ${diff_ops(final_file_name, filediff)}
311 ${diff_ops(final_file_name, filediff)}
312
312
313 </label>
313 </label>
314
314
315 ${diff_menu(filediff, use_comments=use_comments)}
315 ${diff_menu(filediff, use_comments=use_comments)}
316 <table id="file-${h.safeid(h.safe_unicode(filediff.patch['filename']))}" data-f-path="${filediff.patch['filename']}" data-anchor-id="${h.FID(filediff.raw_id, filediff.patch['filename'])}" class="code-visible-block cb cb-diff-${c.user_session_attrs["diffmode"]} code-highlight ${(over_lines_changed_limit and 'cb-collapsed' or '')}">
316 <table id="file-${h.safeid(h.safe_unicode(filediff.patch['filename']))}" data-f-path="${filediff.patch['filename']}" data-anchor-id="${h.FID(filediff.raw_id, filediff.patch['filename'])}" class="code-visible-block cb cb-diff-${c.user_session_attrs["diffmode"]} code-highlight ${(over_lines_changed_limit and 'cb-collapsed' or '')}">
317
317
318 ## new/deleted/empty content case
318 ## new/deleted/empty content case
319 % if not filediff.hunks:
319 % if not filediff.hunks:
320 ## Comment container, on "fakes" hunk that contains all data to render comments
320 ## Comment container, on "fakes" hunk that contains all data to render comments
321 ${render_hunk_lines(filediff, c.user_session_attrs["diffmode"], filediff.hunk_ops, use_comments=use_comments, inline_comments=inline_comments, active_pattern_entries=active_pattern_entries)}
321 ${render_hunk_lines(filediff, c.user_session_attrs["diffmode"], filediff.hunk_ops, use_comments=use_comments, inline_comments=inline_comments, active_pattern_entries=active_pattern_entries)}
322 % endif
322 % endif
323
323
324 %if filediff.limited_diff:
324 %if filediff.limited_diff:
325 <tr class="cb-warning cb-collapser">
325 <tr class="cb-warning cb-collapser">
326 <td class="cb-text" ${(c.user_session_attrs["diffmode"] == 'unified' and 'colspan=4' or 'colspan=6')}>
326 <td class="cb-text" ${(c.user_session_attrs["diffmode"] == 'unified' and 'colspan=4' or 'colspan=6')}>
327 ${_('The requested commit or file is too big and content was truncated.')} <a href="${h.current_route_path(request, fulldiff=1)}" onclick="return confirm('${_("Showing a big diff might take some time and resources, continue?")}')">${_('Show full diff')}</a>
327 ${_('The requested commit or file is too big and content was truncated.')} <a href="${h.current_route_path(request, fulldiff=1)}" onclick="return confirm('${_("Showing a big diff might take some time and resources, continue?")}')">${_('Show full diff')}</a>
328 </td>
328 </td>
329 </tr>
329 </tr>
330 %else:
330 %else:
331 %if over_lines_changed_limit:
331 %if over_lines_changed_limit:
332 <tr class="cb-warning cb-collapser">
332 <tr class="cb-warning cb-collapser">
333 <td class="cb-text" ${(c.user_session_attrs["diffmode"] == 'unified' and 'colspan=4' or 'colspan=6')}>
333 <td class="cb-text" ${(c.user_session_attrs["diffmode"] == 'unified' and 'colspan=4' or 'colspan=6')}>
334 ${_('This diff has been collapsed as it changes many lines, (%i lines changed)' % lines_changed)}
334 ${_('This diff has been collapsed as it changes many lines, (%i lines changed)' % lines_changed)}
335 <a href="#" class="cb-expand"
335 <a href="#" class="cb-expand"
336 onclick="$(this).closest('table').removeClass('cb-collapsed'); updateSticky(); return false;">${_('Show them')}
336 onclick="$(this).closest('table').removeClass('cb-collapsed'); updateSticky(); return false;">${_('Show them')}
337 </a>
337 </a>
338 <a href="#" class="cb-collapse"
338 <a href="#" class="cb-collapse"
339 onclick="$(this).closest('table').addClass('cb-collapsed'); updateSticky(); return false;">${_('Hide them')}
339 onclick="$(this).closest('table').addClass('cb-collapsed'); updateSticky(); return false;">${_('Hide them')}
340 </a>
340 </a>
341 </td>
341 </td>
342 </tr>
342 </tr>
343 %endif
343 %endif
344 %endif
344 %endif
345
345
346 % for hunk in filediff.hunks:
346 % for hunk in filediff.hunks:
347 <tr class="cb-hunk">
347 <tr class="cb-hunk">
348 <td ${(c.user_session_attrs["diffmode"] == 'unified' and 'colspan=3' or '')}>
348 <td ${(c.user_session_attrs["diffmode"] == 'unified' and 'colspan=3' or '')}>
349 ## TODO: dan: add ajax loading of more context here
349 ## TODO: dan: add ajax loading of more context here
350 ## <a href="#">
350 ## <a href="#">
351 <i class="icon-more"></i>
351 <i class="icon-more"></i>
352 ## </a>
352 ## </a>
353 </td>
353 </td>
354 <td ${(c.user_session_attrs["diffmode"] == 'sideside' and 'colspan=5' or '')}>
354 <td ${(c.user_session_attrs["diffmode"] == 'sideside' and 'colspan=5' or '')}>
355 @@
355 @@
356 -${hunk.source_start},${hunk.source_length}
356 -${hunk.source_start},${hunk.source_length}
357 +${hunk.target_start},${hunk.target_length}
357 +${hunk.target_start},${hunk.target_length}
358 ${hunk.section_header}
358 ${hunk.section_header}
359 </td>
359 </td>
360 </tr>
360 </tr>
361
361
362 ${render_hunk_lines(filediff, c.user_session_attrs["diffmode"], hunk, use_comments=use_comments, inline_comments=inline_comments, active_pattern_entries=active_pattern_entries)}
362 ${render_hunk_lines(filediff, c.user_session_attrs["diffmode"], hunk, use_comments=use_comments, inline_comments=inline_comments, active_pattern_entries=active_pattern_entries)}
363 % endfor
363 % endfor
364
364
365 <% unmatched_comments = (inline_comments or {}).get(filediff.patch['filename'], {}) %>
365 <% unmatched_comments = (inline_comments or {}).get(filediff.patch['filename'], {}) %>
366
366
367 ## outdated comments that do not fit into currently displayed lines
367 ## outdated comments that do not fit into currently displayed lines
368 % for lineno, comments in unmatched_comments.items():
368 % for lineno, comments in unmatched_comments.items():
369
369
370 %if c.user_session_attrs["diffmode"] == 'unified':
370 %if c.user_session_attrs["diffmode"] == 'unified':
371 % if loop.index == 0:
371 % if loop.index == 0:
372 <tr class="cb-hunk">
372 <tr class="cb-hunk">
373 <td colspan="3"></td>
373 <td colspan="3"></td>
374 <td>
374 <td>
375 <div>
375 <div>
376 ${_('Unmatched/outdated inline comments below')}
376 ${_('Unmatched/outdated inline comments below')}
377 </div>
377 </div>
378 </td>
378 </td>
379 </tr>
379 </tr>
380 % endif
380 % endif
381 <tr class="cb-line">
381 <tr class="cb-line">
382 <td class="cb-data cb-context"></td>
382 <td class="cb-data cb-context"></td>
383 <td class="cb-lineno cb-context"></td>
383 <td class="cb-lineno cb-context"></td>
384 <td class="cb-lineno cb-context"></td>
384 <td class="cb-lineno cb-context"></td>
385 <td class="cb-content cb-context">
385 <td class="cb-content cb-context">
386 ${inline_comments_container(comments, active_pattern_entries=active_pattern_entries)}
386 ${inline_comments_container(comments, active_pattern_entries=active_pattern_entries)}
387 </td>
387 </td>
388 </tr>
388 </tr>
389 %elif c.user_session_attrs["diffmode"] == 'sideside':
389 %elif c.user_session_attrs["diffmode"] == 'sideside':
390 % if loop.index == 0:
390 % if loop.index == 0:
391 <tr class="cb-comment-info">
391 <tr class="cb-comment-info">
392 <td colspan="2"></td>
392 <td colspan="2"></td>
393 <td class="cb-line">
393 <td class="cb-line">
394 <div>
394 <div>
395 ${_('Unmatched/outdated inline comments below')}
395 ${_('Unmatched/outdated inline comments below')}
396 </div>
396 </div>
397 </td>
397 </td>
398 <td colspan="2"></td>
398 <td colspan="2"></td>
399 <td class="cb-line">
399 <td class="cb-line">
400 <div>
400 <div>
401 ${_('Unmatched/outdated comments below')}
401 ${_('Unmatched/outdated comments below')}
402 </div>
402 </div>
403 </td>
403 </td>
404 </tr>
404 </tr>
405 % endif
405 % endif
406 <tr class="cb-line">
406 <tr class="cb-line">
407 <td class="cb-data cb-context"></td>
407 <td class="cb-data cb-context"></td>
408 <td class="cb-lineno cb-context"></td>
408 <td class="cb-lineno cb-context"></td>
409 <td class="cb-content cb-context">
409 <td class="cb-content cb-context">
410 % if lineno.startswith('o'):
410 % if lineno.startswith('o'):
411 ${inline_comments_container(comments, active_pattern_entries=active_pattern_entries)}
411 ${inline_comments_container(comments, active_pattern_entries=active_pattern_entries)}
412 % endif
412 % endif
413 </td>
413 </td>
414
414
415 <td class="cb-data cb-context"></td>
415 <td class="cb-data cb-context"></td>
416 <td class="cb-lineno cb-context"></td>
416 <td class="cb-lineno cb-context"></td>
417 <td class="cb-content cb-context">
417 <td class="cb-content cb-context">
418 % if lineno.startswith('n'):
418 % if lineno.startswith('n'):
419 ${inline_comments_container(comments, active_pattern_entries=active_pattern_entries)}
419 ${inline_comments_container(comments, active_pattern_entries=active_pattern_entries)}
420 % endif
420 % endif
421 </td>
421 </td>
422 </tr>
422 </tr>
423 %endif
423 %endif
424
424
425 % endfor
425 % endfor
426
426
427 </table>
427 </table>
428 </div>
428 </div>
429 %endfor
429 %endfor
430
430
431 ## outdated comments that are made for a file that has been deleted
431 ## outdated comments that are made for a file that has been deleted
432 % for filename, comments_dict in (deleted_files_comments or {}).items():
432 % for filename, comments_dict in (deleted_files_comments or {}).items():
433
433
434 <%
434 <%
435 display_state = 'display: none'
435 display_state = 'display: none'
436 open_comments_in_file = [x for x in comments_dict['comments'] if x.outdated is False]
436 open_comments_in_file = [x for x in comments_dict['comments'] if x.outdated is False]
437 if open_comments_in_file:
437 if open_comments_in_file:
438 display_state = ''
438 display_state = ''
439 fid = str(id(filename))
439 fid = str(id(filename))
440 %>
440 %>
441 <div class="filediffs filediff-outdated" style="${display_state}">
441 <div class="filediffs filediff-outdated" style="${display_state}">
442 <input ${(collapse_all and 'checked' or '')} class="filediff-collapse-state collapse-${diffset_container_id}" id="filediff-collapse-${id(filename)}" type="checkbox" onchange="updateSticky();">
442 <input ${(collapse_all and 'checked' or '')} class="filediff-collapse-state collapse-${diffset_container_id}" id="filediff-collapse-${id(filename)}" type="checkbox" onchange="updateSticky();">
443 <div class="filediff" data-f-path="${filename}" id="a_${h.FID(fid, filename)}">
443 <div class="filediff" data-f-path="${filename}" id="a_${h.FID(fid, filename)}">
444 <label for="filediff-collapse-${id(filename)}" class="filediff-heading">
444 <label for="filediff-collapse-${id(filename)}" class="filediff-heading">
445 <div class="filediff-collapse-indicator icon-"></div>
445 <div class="filediff-collapse-indicator icon-"></div>
446
446
447 <span class="pill">
447 <span class="pill">
448 ## file was deleted
448 ## file was deleted
449 ${filename}
449 ${filename}
450 </span>
450 </span>
451 <span class="pill-group pull-left" >
451 <span class="pill-group pull-left" >
452 ## file op, doesn't need translation
452 ## file op, doesn't need translation
453 <span class="pill" op="removed">unresolved comments</span>
453 <span class="pill" op="removed">unresolved comments</span>
454 </span>
454 </span>
455 <a class="pill filediff-anchor" href="#a_${h.FID(fid, filename)}"></a>
455 <a class="pill filediff-anchor" href="#a_${h.FID(fid, filename)}"></a>
456 <span class="pill-group pull-right">
456 <span class="pill-group pull-right">
457 <span class="pill" op="deleted">
457 <span class="pill" op="deleted">
458 % if comments_dict['stats'] >0:
458 % if comments_dict['stats'] >0:
459 -${comments_dict['stats']}
459 -${comments_dict['stats']}
460 % else:
460 % else:
461 ${comments_dict['stats']}
461 ${comments_dict['stats']}
462 % endif
462 % endif
463 </span>
463 </span>
464 </span>
464 </span>
465 </label>
465 </label>
466
466
467 <table class="cb cb-diff-${c.user_session_attrs["diffmode"]} code-highlight ${(over_lines_changed_limit and 'cb-collapsed' or '')}">
467 <table class="cb cb-diff-${c.user_session_attrs["diffmode"]} code-highlight ${(over_lines_changed_limit and 'cb-collapsed' or '')}">
468 <tr>
468 <tr>
469 % if c.user_session_attrs["diffmode"] == 'unified':
469 % if c.user_session_attrs["diffmode"] == 'unified':
470 <td></td>
470 <td></td>
471 %endif
471 %endif
472
472
473 <td></td>
473 <td></td>
474 <td class="cb-text cb-${op_class(BIN_FILENODE)}" ${(c.user_session_attrs["diffmode"] == 'unified' and 'colspan=4' or 'colspan=5')}>
474 <td class="cb-text cb-${op_class(BIN_FILENODE)}" ${(c.user_session_attrs["diffmode"] == 'unified' and 'colspan=4' or 'colspan=5')}>
475 <strong>${_('This file was removed from diff during updates to this pull-request.')}</strong><br/>
475 <strong>${_('This file was removed from diff during updates to this pull-request.')}</strong><br/>
476 ${_('There are still outdated/unresolved comments attached to it.')}
476 ${_('There are still outdated/unresolved comments attached to it.')}
477 </td>
477 </td>
478 </tr>
478 </tr>
479 %if c.user_session_attrs["diffmode"] == 'unified':
479 %if c.user_session_attrs["diffmode"] == 'unified':
480 <tr class="cb-line">
480 <tr class="cb-line">
481 <td class="cb-data cb-context"></td>
481 <td class="cb-data cb-context"></td>
482 <td class="cb-lineno cb-context"></td>
482 <td class="cb-lineno cb-context"></td>
483 <td class="cb-lineno cb-context"></td>
483 <td class="cb-lineno cb-context"></td>
484 <td class="cb-content cb-context">
484 <td class="cb-content cb-context">
485 ${inline_comments_container(comments_dict['comments'], active_pattern_entries=active_pattern_entries)}
485 ${inline_comments_container(comments_dict['comments'], active_pattern_entries=active_pattern_entries)}
486 </td>
486 </td>
487 </tr>
487 </tr>
488 %elif c.user_session_attrs["diffmode"] == 'sideside':
488 %elif c.user_session_attrs["diffmode"] == 'sideside':
489 <tr class="cb-line">
489 <tr class="cb-line">
490 <td class="cb-data cb-context"></td>
490 <td class="cb-data cb-context"></td>
491 <td class="cb-lineno cb-context"></td>
491 <td class="cb-lineno cb-context"></td>
492 <td class="cb-content cb-context"></td>
492 <td class="cb-content cb-context"></td>
493
493
494 <td class="cb-data cb-context"></td>
494 <td class="cb-data cb-context"></td>
495 <td class="cb-lineno cb-context"></td>
495 <td class="cb-lineno cb-context"></td>
496 <td class="cb-content cb-context">
496 <td class="cb-content cb-context">
497 ${inline_comments_container(comments_dict['comments'], active_pattern_entries=active_pattern_entries)}
497 ${inline_comments_container(comments_dict['comments'], active_pattern_entries=active_pattern_entries)}
498 </td>
498 </td>
499 </tr>
499 </tr>
500 %endif
500 %endif
501 </table>
501 </table>
502 </div>
502 </div>
503 </div>
503 </div>
504 % endfor
504 % endfor
505
505
506 </div>
506 </div>
507 </div>
507 </div>
508 </%def>
508 </%def>
509
509
510 <%def name="diff_ops(file_name, filediff)">
510 <%def name="diff_ops(file_name, filediff)">
511 <%
511 <%
512 from rhodecode.lib.diffs import NEW_FILENODE, DEL_FILENODE, \
512 from rhodecode.lib.diffs import NEW_FILENODE, DEL_FILENODE, \
513 MOD_FILENODE, RENAMED_FILENODE, CHMOD_FILENODE, BIN_FILENODE, COPIED_FILENODE
513 MOD_FILENODE, RENAMED_FILENODE, CHMOD_FILENODE, BIN_FILENODE, COPIED_FILENODE
514 %>
514 %>
515 <span class="pill">
515 <span class="pill">
516 <i class="icon-file-text"></i>
516 <i class="icon-file-text"></i>
517 ${file_name}
517 ${file_name}
518 </span>
518 </span>
519
519
520 <span class="pill-group pull-right">
520 <span class="pill-group pull-right">
521
521
522 ## ops pills
522 ## ops pills
523 %if filediff.limited_diff:
523 %if filediff.limited_diff:
524 <span class="pill tooltip" op="limited" title="The stats for this diff are not complete">limited diff</span>
524 <span class="pill tooltip" op="limited" title="The stats for this diff are not complete">limited diff</span>
525 %endif
525 %endif
526
526
527 %if NEW_FILENODE in filediff.patch['stats']['ops']:
527 %if NEW_FILENODE in filediff.patch['stats']['ops']:
528 <span class="pill" op="created">created</span>
528 <span class="pill" op="created">created</span>
529 %if filediff['target_mode'].startswith('120'):
529 %if filediff['target_mode'].startswith('120'):
530 <span class="pill" op="symlink">symlink</span>
530 <span class="pill" op="symlink">symlink</span>
531 %else:
531 %else:
532 <span class="pill" op="mode">${nice_mode(filediff['target_mode'])}</span>
532 <span class="pill" op="mode">${nice_mode(filediff['target_mode'])}</span>
533 %endif
533 %endif
534 %endif
534 %endif
535
535
536 %if RENAMED_FILENODE in filediff.patch['stats']['ops']:
536 %if RENAMED_FILENODE in filediff.patch['stats']['ops']:
537 <span class="pill" op="renamed">renamed</span>
537 <span class="pill" op="renamed">renamed</span>
538 %endif
538 %endif
539
539
540 %if COPIED_FILENODE in filediff.patch['stats']['ops']:
540 %if COPIED_FILENODE in filediff.patch['stats']['ops']:
541 <span class="pill" op="copied">copied</span>
541 <span class="pill" op="copied">copied</span>
542 %endif
542 %endif
543
543
544 %if DEL_FILENODE in filediff.patch['stats']['ops']:
544 %if DEL_FILENODE in filediff.patch['stats']['ops']:
545 <span class="pill" op="removed">removed</span>
545 <span class="pill" op="removed">removed</span>
546 %endif
546 %endif
547
547
548 %if CHMOD_FILENODE in filediff.patch['stats']['ops']:
548 %if CHMOD_FILENODE in filediff.patch['stats']['ops']:
549 <span class="pill" op="mode">
549 <span class="pill" op="mode">
550 ${nice_mode(filediff['source_mode'])}${nice_mode(filediff['target_mode'])}
550 ${nice_mode(filediff['source_mode'])}${nice_mode(filediff['target_mode'])}
551 </span>
551 </span>
552 %endif
552 %endif
553
553
554 %if BIN_FILENODE in filediff.patch['stats']['ops']:
554 %if BIN_FILENODE in filediff.patch['stats']['ops']:
555 <span class="pill" op="binary">binary</span>
555 <span class="pill" op="binary">binary</span>
556 %if MOD_FILENODE in filediff.patch['stats']['ops']:
556 %if MOD_FILENODE in filediff.patch['stats']['ops']:
557 <span class="pill" op="modified">modified</span>
557 <span class="pill" op="modified">modified</span>
558 %endif
558 %endif
559 %endif
559 %endif
560
560
561 <span class="pill" op="added">${('+' if filediff.patch['stats']['added'] else '')}${filediff.patch['stats']['added']}</span>
561 <span class="pill" op="added">${('+' if filediff.patch['stats']['added'] else '')}${filediff.patch['stats']['added']}</span>
562 <span class="pill" op="deleted">${((h.safe_int(filediff.patch['stats']['deleted']) or 0) * -1)}</span>
562 <span class="pill" op="deleted">${((h.safe_int(filediff.patch['stats']['deleted']) or 0) * -1)}</span>
563
563
564 </span>
564 </span>
565
565
566 </%def>
566 </%def>
567
567
568 <%def name="nice_mode(filemode)">
568 <%def name="nice_mode(filemode)">
569 ${(filemode.startswith('100') and filemode[3:] or filemode)}
569 ${(filemode.startswith('100') and filemode[3:] or filemode)}
570 </%def>
570 </%def>
571
571
572 <%def name="diff_menu(filediff, use_comments=False)">
572 <%def name="diff_menu(filediff, use_comments=False)">
573 <div class="filediff-menu">
573 <div class="filediff-menu">
574
574
575 %if filediff.diffset.source_ref:
575 %if filediff.diffset.source_ref:
576
576
577 ## FILE BEFORE CHANGES
577 ## FILE BEFORE CHANGES
578 %if filediff.operation in ['D', 'M']:
578 %if filediff.operation in ['D', 'M']:
579 <a
579 <a
580 class="tooltip"
580 class="tooltip"
581 href="${h.route_path('repo_files',repo_name=filediff.diffset.target_repo_name,commit_id=filediff.diffset.source_ref,f_path=filediff.source_file_path)}"
581 href="${h.route_path('repo_files',repo_name=filediff.diffset.target_repo_name,commit_id=filediff.diffset.source_ref,f_path=filediff.source_file_path)}"
582 title="${h.tooltip(_('Show file at commit: %(commit_id)s') % {'commit_id': filediff.diffset.source_ref[:12]})}"
582 title="${h.tooltip(_('Show file at commit: %(commit_id)s') % {'commit_id': filediff.diffset.source_ref[:12]})}"
583 >
583 >
584 ${_('Show file before')}
584 ${_('Show file before')}
585 </a> |
585 </a> |
586 %else:
586 %else:
587 <span
587 <span
588 class="tooltip"
588 class="tooltip"
589 title="${h.tooltip(_('File not present at commit: %(commit_id)s') % {'commit_id': filediff.diffset.source_ref[:12]})}"
589 title="${h.tooltip(_('File not present at commit: %(commit_id)s') % {'commit_id': filediff.diffset.source_ref[:12]})}"
590 >
590 >
591 ${_('Show file before')}
591 ${_('Show file before')}
592 </span> |
592 </span> |
593 %endif
593 %endif
594
594
595 ## FILE AFTER CHANGES
595 ## FILE AFTER CHANGES
596 %if filediff.operation in ['A', 'M']:
596 %if filediff.operation in ['A', 'M']:
597 <a
597 <a
598 class="tooltip"
598 class="tooltip"
599 href="${h.route_path('repo_files',repo_name=filediff.diffset.source_repo_name,commit_id=filediff.diffset.target_ref,f_path=filediff.target_file_path)}"
599 href="${h.route_path('repo_files',repo_name=filediff.diffset.source_repo_name,commit_id=filediff.diffset.target_ref,f_path=filediff.target_file_path)}"
600 title="${h.tooltip(_('Show file at commit: %(commit_id)s') % {'commit_id': filediff.diffset.target_ref[:12]})}"
600 title="${h.tooltip(_('Show file at commit: %(commit_id)s') % {'commit_id': filediff.diffset.target_ref[:12]})}"
601 >
601 >
602 ${_('Show file after')}
602 ${_('Show file after')}
603 </a>
603 </a>
604 %else:
604 %else:
605 <span
605 <span
606 class="tooltip"
606 class="tooltip"
607 title="${h.tooltip(_('File not present at commit: %(commit_id)s') % {'commit_id': filediff.diffset.target_ref[:12]})}"
607 title="${h.tooltip(_('File not present at commit: %(commit_id)s') % {'commit_id': filediff.diffset.target_ref[:12]})}"
608 >
608 >
609 ${_('Show file after')}
609 ${_('Show file after')}
610 </span>
610 </span>
611 %endif
611 %endif
612
612
613 % if use_comments:
613 % if use_comments:
614 |
614 |
615 <a href="#" onclick="Rhodecode.comments.toggleDiffComments(this);return toggleElement(this)"
615 <a href="#" onclick="Rhodecode.comments.toggleDiffComments(this);return toggleElement(this)"
616 data-toggle-on="${_('Hide comments')}"
616 data-toggle-on="${_('Hide comments')}"
617 data-toggle-off="${_('Show comments')}">
617 data-toggle-off="${_('Show comments')}">
618 <span class="hide-comment-button">${_('Hide comments')}</span>
618 <span class="hide-comment-button">${_('Hide comments')}</span>
619 </a>
619 </a>
620 % endif
620 % endif
621
621
622 %endif
622 %endif
623
623
624 </div>
624 </div>
625 </%def>
625 </%def>
626
626
627
627
628 <%def name="inline_comments_container(comments, active_pattern_entries=None, line_no='', f_path='')">
628 <%def name="inline_comments_container(comments, active_pattern_entries=None, line_no='', f_path='')">
629
629
630 <div class="inline-comments">
630 <div class="inline-comments">
631 %for comment in comments:
631 %for comment in comments:
632 ${commentblock.comment_block(comment, inline=True, active_pattern_entries=active_pattern_entries)}
632 ${commentblock.comment_block(comment, inline=True, active_pattern_entries=active_pattern_entries)}
633 %endfor
633 %endfor
634
634
635 <%
635 <%
636 extra_class = ''
636 extra_class = ''
637 extra_style = ''
637 extra_style = ''
638
638
639 if comments and comments[-1].outdated_at_version(c.at_version_num):
639 if comments and comments[-1].outdated_at_version(c.at_version_num):
640 extra_class = ' comment-outdated'
640 extra_class = ' comment-outdated'
641 extra_style = 'display: none;'
641 extra_style = 'display: none;'
642
642
643 %>
643 %>
644
644
645 <div class="reply-thread-container-wrapper${extra_class}" style="${extra_style}">
645 <div class="reply-thread-container-wrapper${extra_class}" style="${extra_style}">
646 <div class="reply-thread-container${extra_class}">
646 <div class="reply-thread-container${extra_class}">
647 <div class="reply-thread-gravatar">
647 <div class="reply-thread-gravatar">
648 % if c.rhodecode_user.username != h.DEFAULT_USER:
648 % if c.rhodecode_user.username != h.DEFAULT_USER:
649 ${base.gravatar(c.rhodecode_user.email, 20, tooltip=True, user=c.rhodecode_user)}
649 ${base.gravatar(c.rhodecode_user.email, 20, tooltip=True, user=c.rhodecode_user)}
650 % endif
650 % endif
651 </div>
651 </div>
652
652
653 <div class="reply-thread-reply-button">
653 <div class="reply-thread-reply-button">
654 % if c.rhodecode_user.username != h.DEFAULT_USER:
654 % if c.rhodecode_user.username != h.DEFAULT_USER:
655 ## initial reply button, some JS logic can append here a FORM to leave a first comment.
655 ## initial reply button, some JS logic can append here a FORM to leave a first comment.
656 <button class="cb-comment-add-button" onclick="return Rhodecode.comments.createComment(this, '${f_path}', '${line_no}', null)">Reply...</button>
656 <button class="cb-comment-add-button" onclick="return Rhodecode.comments.createComment(this, '${f_path}', '${line_no}', null)">Reply...</button>
657 % endif
657 % endif
658 </div>
658 </div>
659 ##% endif
659 ##% endif
660 <div class="reply-thread-last"></div>
660 <div class="reply-thread-last"></div>
661 </div>
661 </div>
662 </div>
662 </div>
663 </div>
663 </div>
664
664
665 </%def>
665 </%def>
666
666
667 <%!
667 <%!
668
668
669 def get_inline_comments(comments, filename):
669 def get_inline_comments(comments, filename):
670 if hasattr(filename, 'unicode_path'):
670 if hasattr(filename, 'unicode_path'):
671 filename = filename.unicode_path
671 filename = filename.unicode_path
672
672
673 if not isinstance(filename, (unicode, str)):
673 if not isinstance(filename, (unicode, str)):
674 return None
674 return None
675
675
676 if comments and filename in comments:
676 if comments and filename in comments:
677 return comments[filename]
677 return comments[filename]
678
678
679 return None
679 return None
680
680
681 def get_comments_for(diff_type, comments, filename, line_version, line_number):
681 def get_comments_for(diff_type, comments, filename, line_version, line_number):
682 if hasattr(filename, 'unicode_path'):
682 if hasattr(filename, 'unicode_path'):
683 filename = filename.unicode_path
683 filename = filename.unicode_path
684
684
685 if not isinstance(filename, (unicode, str)):
685 if not isinstance(filename, (unicode, str)):
686 return None
686 return None
687
687
688 file_comments = get_inline_comments(comments, filename)
688 file_comments = get_inline_comments(comments, filename)
689 if file_comments is None:
689 if file_comments is None:
690 return None
690 return None
691
691
692 line_key = '{}{}'.format(line_version, line_number) ## e.g o37, n12
692 line_key = '{}{}'.format(line_version, line_number) ## e.g o37, n12
693 if line_key in file_comments:
693 if line_key in file_comments:
694 data = file_comments.pop(line_key)
694 data = file_comments.pop(line_key)
695 return data
695 return data
696 %>
696 %>
697
697
698 <%def name="render_hunk_lines_sideside(filediff, hunk, use_comments=False, inline_comments=None, active_pattern_entries=None)">
698 <%def name="render_hunk_lines_sideside(filediff, hunk, use_comments=False, inline_comments=None, active_pattern_entries=None)">
699
699
700 <% chunk_count = 1 %>
700 <% chunk_count = 1 %>
701 %for loop_obj, item in h.looper(hunk.sideside):
701 %for loop_obj, item in h.looper(hunk.sideside):
702 <%
702 <%
703 line = item
703 line = item
704 i = loop_obj.index
704 i = loop_obj.index
705 prev_line = loop_obj.previous
705 prev_line = loop_obj.previous
706 old_line_anchor, new_line_anchor = None, None
706 old_line_anchor, new_line_anchor = None, None
707
707
708 if line.original.lineno:
708 if line.original.lineno:
709 old_line_anchor = diff_line_anchor(filediff.raw_id, hunk.source_file_path, line.original.lineno, 'o')
709 old_line_anchor = diff_line_anchor(filediff.raw_id, hunk.source_file_path, line.original.lineno, 'o')
710 if line.modified.lineno:
710 if line.modified.lineno:
711 new_line_anchor = diff_line_anchor(filediff.raw_id, hunk.target_file_path, line.modified.lineno, 'n')
711 new_line_anchor = diff_line_anchor(filediff.raw_id, hunk.target_file_path, line.modified.lineno, 'n')
712
712
713 line_action = line.modified.action or line.original.action
713 line_action = line.modified.action or line.original.action
714 prev_line_action = prev_line and (prev_line.modified.action or prev_line.original.action)
714 prev_line_action = prev_line and (prev_line.modified.action or prev_line.original.action)
715 %>
715 %>
716
716
717 <tr class="cb-line">
717 <tr class="cb-line">
718 <td class="cb-data ${action_class(line.original.action)}"
718 <td class="cb-data ${action_class(line.original.action)}"
719 data-line-no="${line.original.lineno}"
719 data-line-no="${line.original.lineno}"
720 >
720 >
721
721
722 <% line_old_comments, line_old_comments_no_drafts = None, None %>
722 <% line_old_comments, line_old_comments_no_drafts = None, None %>
723 %if line.original.get_comment_args:
723 %if line.original.get_comment_args:
724 <%
724 <%
725 line_old_comments = get_comments_for('side-by-side', inline_comments, *line.original.get_comment_args)
725 line_old_comments = get_comments_for('side-by-side', inline_comments, *line.original.get_comment_args)
726 line_old_comments_no_drafts = [c for c in line_old_comments if not c.draft] if line_old_comments else []
726 line_old_comments_no_drafts = [c for c in line_old_comments if not c.draft] if line_old_comments else []
727 has_outdated = any([x.outdated for x in line_old_comments_no_drafts])
727 has_outdated = any([x.outdated for x in line_old_comments_no_drafts])
728 %>
728 %>
729 %endif
729 %endif
730 %if line_old_comments_no_drafts:
730 %if line_old_comments_no_drafts:
731 % if has_outdated:
731 % if has_outdated:
732 <i class="tooltip toggle-comment-action icon-comment-toggle" title="${_('Comments including outdated: {}. Click here to toggle them.').format(len(line_old_comments_no_drafts))}" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
732 <i class="tooltip toggle-comment-action icon-comment-toggle" title="${_('Comments including outdated: {}. Click here to toggle them.').format(len(line_old_comments_no_drafts))}" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
733 % else:
733 % else:
734 <i class="tooltip toggle-comment-action icon-comment" title="${_('Comments: {}. Click to toggle them.').format(len(line_old_comments_no_drafts))}" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
734 <i class="tooltip toggle-comment-action icon-comment" title="${_('Comments: {}. Click to toggle them.').format(len(line_old_comments_no_drafts))}" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
735 % endif
735 % endif
736 %endif
736 %endif
737 </td>
737 </td>
738 <td class="cb-lineno ${action_class(line.original.action)}"
738 <td class="cb-lineno ${action_class(line.original.action)}"
739 data-line-no="${line.original.lineno}"
739 data-line-no="${line.original.lineno}"
740 %if old_line_anchor:
740 %if old_line_anchor:
741 id="${old_line_anchor}"
741 id="${old_line_anchor}"
742 %endif
742 %endif
743 >
743 >
744 %if line.original.lineno:
744 %if line.original.lineno:
745 <a name="${old_line_anchor}" href="#${old_line_anchor}">${line.original.lineno}</a>
745 <a name="${old_line_anchor}" href="#${old_line_anchor}">${line.original.lineno}</a>
746 %endif
746 %endif
747 </td>
747 </td>
748
748
749 <% line_no = 'o{}'.format(line.original.lineno) %>
749 <% line_no = 'o{}'.format(line.original.lineno) %>
750 <td class="cb-content ${action_class(line.original.action)}"
750 <td class="cb-content ${action_class(line.original.action)}"
751 data-line-no="${line_no}"
751 data-line-no="${line_no}"
752 >
752 >
753 %if use_comments and line.original.lineno:
753 %if use_comments and line.original.lineno:
754 ${render_add_comment_button(line_no=line_no, f_path=filediff.patch['filename'])}
754 ${render_add_comment_button(line_no=line_no, f_path=filediff.patch['filename'])}
755 %endif
755 %endif
756 <span class="cb-code"><span class="cb-action ${action_class(line.original.action)}"></span>${line.original.content or '' | n}</span>
756 <span class="cb-code"><span class="cb-action ${action_class(line.original.action)}"></span>${line.original.content or '' | n}</span>
757
757
758 %if use_comments and line.original.lineno and line_old_comments:
758 %if use_comments and line.original.lineno and line_old_comments:
759 ${inline_comments_container(line_old_comments, active_pattern_entries=active_pattern_entries, line_no=line_no, f_path=filediff.patch['filename'])}
759 ${inline_comments_container(line_old_comments, active_pattern_entries=active_pattern_entries, line_no=line_no, f_path=filediff.patch['filename'])}
760 %endif
760 %endif
761
761
762 </td>
762 </td>
763 <td class="cb-data ${action_class(line.modified.action)}"
763 <td class="cb-data ${action_class(line.modified.action)}"
764 data-line-no="${line.modified.lineno}"
764 data-line-no="${line.modified.lineno}"
765 >
765 >
766 <div>
766 <div>
767
767
768 <% line_new_comments, line_new_comments_no_drafts = None, None %>
768 <% line_new_comments, line_new_comments_no_drafts = None, None %>
769 %if line.modified.get_comment_args:
769 %if line.modified.get_comment_args:
770 <%
770 <%
771 line_new_comments = get_comments_for('side-by-side', inline_comments, *line.modified.get_comment_args)
771 line_new_comments = get_comments_for('side-by-side', inline_comments, *line.modified.get_comment_args)
772 line_new_comments_no_drafts = [c for c in line_new_comments if not c.draft] if line_new_comments else []
772 line_new_comments_no_drafts = [c for c in line_new_comments if not c.draft] if line_new_comments else []
773 has_outdated = any([x.outdated for x in line_new_comments_no_drafts])
773 has_outdated = any([x.outdated for x in line_new_comments_no_drafts])
774 %>
774 %>
775 %endif
775 %endif
776
776
777 %if line_new_comments_no_drafts:
777 %if line_new_comments_no_drafts:
778 % if has_outdated:
778 % if has_outdated:
779 <i class="tooltip toggle-comment-action icon-comment-toggle" title="${_('Comments including outdated: {}. Click here to toggle them.').format(len(line_new_comments_no_drafts))}" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
779 <i class="tooltip toggle-comment-action icon-comment-toggle" title="${_('Comments including outdated: {}. Click here to toggle them.').format(len(line_new_comments_no_drafts))}" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
780 % else:
780 % else:
781 <i class="tooltip toggle-comment-action icon-comment" title="${_('Comments: {}. Click to toggle them.').format(len(line_new_comments_no_drafts))}" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
781 <i class="tooltip toggle-comment-action icon-comment" title="${_('Comments: {}. Click to toggle them.').format(len(line_new_comments_no_drafts))}" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
782 % endif
782 % endif
783 %endif
783 %endif
784 </div>
784 </div>
785 </td>
785 </td>
786 <td class="cb-lineno ${action_class(line.modified.action)}"
786 <td class="cb-lineno ${action_class(line.modified.action)}"
787 data-line-no="${line.modified.lineno}"
787 data-line-no="${line.modified.lineno}"
788 %if new_line_anchor:
788 %if new_line_anchor:
789 id="${new_line_anchor}"
789 id="${new_line_anchor}"
790 %endif
790 %endif
791 >
791 >
792 %if line.modified.lineno:
792 %if line.modified.lineno:
793 <a name="${new_line_anchor}" href="#${new_line_anchor}">${line.modified.lineno}</a>
793 <a name="${new_line_anchor}" href="#${new_line_anchor}">${line.modified.lineno}</a>
794 %endif
794 %endif
795 </td>
795 </td>
796
796
797 <% line_no = 'n{}'.format(line.modified.lineno) %>
797 <% line_no = 'n{}'.format(line.modified.lineno) %>
798 <td class="cb-content ${action_class(line.modified.action)}"
798 <td class="cb-content ${action_class(line.modified.action)}"
799 data-line-no="${line_no}"
799 data-line-no="${line_no}"
800 >
800 >
801 %if use_comments and line.modified.lineno:
801 %if use_comments and line.modified.lineno:
802 ${render_add_comment_button(line_no=line_no, f_path=filediff.patch['filename'])}
802 ${render_add_comment_button(line_no=line_no, f_path=filediff.patch['filename'])}
803 %endif
803 %endif
804 <span class="cb-code"><span class="cb-action ${action_class(line.modified.action)}"></span>${line.modified.content or '' | n}</span>
804 <span class="cb-code"><span class="cb-action ${action_class(line.modified.action)}"></span>${line.modified.content or '' | n}</span>
805 % if line_action in ['+', '-'] and prev_line_action not in ['+', '-']:
805 % if line_action in ['+', '-'] and prev_line_action not in ['+', '-']:
806 <div class="nav-chunk" style="visibility: hidden">
806 <div class="nav-chunk" style="visibility: hidden">
807 <i class="icon-eye" title="viewing diff hunk-${hunk.index}-${chunk_count}"></i>
807 <i class="icon-eye" title="viewing diff hunk-${hunk.index}-${chunk_count}"></i>
808 </div>
808 </div>
809 <% chunk_count +=1 %>
809 <% chunk_count +=1 %>
810 % endif
810 % endif
811 %if use_comments and line.modified.lineno and line_new_comments:
811 %if use_comments and line.modified.lineno and line_new_comments:
812 ${inline_comments_container(line_new_comments, active_pattern_entries=active_pattern_entries, line_no=line_no, f_path=filediff.patch['filename'])}
812 ${inline_comments_container(line_new_comments, active_pattern_entries=active_pattern_entries, line_no=line_no, f_path=filediff.patch['filename'])}
813 %endif
813 %endif
814
814
815 </td>
815 </td>
816 </tr>
816 </tr>
817 %endfor
817 %endfor
818 </%def>
818 </%def>
819
819
820
820
821 <%def name="render_hunk_lines_unified(filediff, hunk, use_comments=False, inline_comments=None, active_pattern_entries=None)">
821 <%def name="render_hunk_lines_unified(filediff, hunk, use_comments=False, inline_comments=None, active_pattern_entries=None)">
822 %for old_line_no, new_line_no, action, content, comments_args in hunk.unified:
822 %for old_line_no, new_line_no, action, content, comments_args in hunk.unified:
823
823
824 <%
824 <%
825 old_line_anchor, new_line_anchor = None, None
825 old_line_anchor, new_line_anchor = None, None
826 if old_line_no:
826 if old_line_no:
827 old_line_anchor = diff_line_anchor(filediff.raw_id, hunk.source_file_path, old_line_no, 'o')
827 old_line_anchor = diff_line_anchor(filediff.raw_id, hunk.source_file_path, old_line_no, 'o')
828 if new_line_no:
828 if new_line_no:
829 new_line_anchor = diff_line_anchor(filediff.raw_id, hunk.target_file_path, new_line_no, 'n')
829 new_line_anchor = diff_line_anchor(filediff.raw_id, hunk.target_file_path, new_line_no, 'n')
830 %>
830 %>
831 <tr class="cb-line">
831 <tr class="cb-line">
832 <td class="cb-data ${action_class(action)}">
832 <td class="cb-data ${action_class(action)}">
833 <div>
833 <div>
834
834
835 <% comments, comments_no_drafts = None, None %>
835 <% comments, comments_no_drafts = None, None %>
836 %if comments_args:
836 %if comments_args:
837 <%
837 <%
838 comments = get_comments_for('unified', inline_comments, *comments_args)
838 comments = get_comments_for('unified', inline_comments, *comments_args)
839 comments_no_drafts = [c for c in line_new_comments if not c.draft] if line_new_comments else []
839 comments_no_drafts = [c for c in line_new_comments if not c.draft] if line_new_comments else []
840 has_outdated = any([x.outdated for x in comments_no_drafts])
840 has_outdated = any([x.outdated for x in comments_no_drafts])
841 %>
841 %>
842 %endif
842 %endif
843
843
844 % if comments_no_drafts:
844 % if comments_no_drafts:
845 % if has_outdated:
845 % if has_outdated:
846 <i class="tooltip toggle-comment-action icon-comment-toggle" title="${_('Comments including outdated: {}. Click here to toggle them.').format(len(comments_no_drafts))}" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
846 <i class="tooltip toggle-comment-action icon-comment-toggle" title="${_('Comments including outdated: {}. Click here to toggle them.').format(len(comments_no_drafts))}" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
847 % else:
847 % else:
848 <i class="tooltip toggle-comment-action icon-comment" title="${_('Comments: {}. Click to toggle them.').format(len(comments_no_drafts))}" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
848 <i class="tooltip toggle-comment-action icon-comment" title="${_('Comments: {}. Click to toggle them.').format(len(comments_no_drafts))}" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
849 % endif
849 % endif
850 % endif
850 % endif
851 </div>
851 </div>
852 </td>
852 </td>
853 <td class="cb-lineno ${action_class(action)}"
853 <td class="cb-lineno ${action_class(action)}"
854 data-line-no="${old_line_no}"
854 data-line-no="${old_line_no}"
855 %if old_line_anchor:
855 %if old_line_anchor:
856 id="${old_line_anchor}"
856 id="${old_line_anchor}"
857 %endif
857 %endif
858 >
858 >
859 %if old_line_anchor:
859 %if old_line_anchor:
860 <a name="${old_line_anchor}" href="#${old_line_anchor}">${old_line_no}</a>
860 <a name="${old_line_anchor}" href="#${old_line_anchor}">${old_line_no}</a>
861 %endif
861 %endif
862 </td>
862 </td>
863 <td class="cb-lineno ${action_class(action)}"
863 <td class="cb-lineno ${action_class(action)}"
864 data-line-no="${new_line_no}"
864 data-line-no="${new_line_no}"
865 %if new_line_anchor:
865 %if new_line_anchor:
866 id="${new_line_anchor}"
866 id="${new_line_anchor}"
867 %endif
867 %endif
868 >
868 >
869 %if new_line_anchor:
869 %if new_line_anchor:
870 <a name="${new_line_anchor}" href="#${new_line_anchor}">${new_line_no}</a>
870 <a name="${new_line_anchor}" href="#${new_line_anchor}">${new_line_no}</a>
871 %endif
871 %endif
872 </td>
872 </td>
873 <% line_no = '{}{}'.format(new_line_no and 'n' or 'o', new_line_no or old_line_no) %>
873 <% line_no = '{}{}'.format(new_line_no and 'n' or 'o', new_line_no or old_line_no) %>
874 <td class="cb-content ${action_class(action)}"
874 <td class="cb-content ${action_class(action)}"
875 data-line-no="${line_no}"
875 data-line-no="${line_no}"
876 >
876 >
877 %if use_comments:
877 %if use_comments:
878 ${render_add_comment_button(line_no=line_no, f_path=filediff.patch['filename'])}
878 ${render_add_comment_button(line_no=line_no, f_path=filediff.patch['filename'])}
879 %endif
879 %endif
880 <span class="cb-code"><span class="cb-action ${action_class(action)}"></span> ${content or '' | n}</span>
880 <span class="cb-code"><span class="cb-action ${action_class(action)}"></span> ${content or '' | n}</span>
881 %if use_comments and comments:
881 %if use_comments and comments:
882 ${inline_comments_container(comments, active_pattern_entries=active_pattern_entries, line_no=line_no, f_path=filediff.patch['filename'])}
882 ${inline_comments_container(comments, active_pattern_entries=active_pattern_entries, line_no=line_no, f_path=filediff.patch['filename'])}
883 %endif
883 %endif
884 </td>
884 </td>
885 </tr>
885 </tr>
886 %endfor
886 %endfor
887 </%def>
887 </%def>
888
888
889
889
890 <%def name="render_hunk_lines(filediff, diff_mode, hunk, use_comments, inline_comments, active_pattern_entries)">
890 <%def name="render_hunk_lines(filediff, diff_mode, hunk, use_comments, inline_comments, active_pattern_entries)">
891 % if diff_mode == 'unified':
891 % if diff_mode == 'unified':
892 ${render_hunk_lines_unified(filediff, hunk, use_comments=use_comments, inline_comments=inline_comments, active_pattern_entries=active_pattern_entries)}
892 ${render_hunk_lines_unified(filediff, hunk, use_comments=use_comments, inline_comments=inline_comments, active_pattern_entries=active_pattern_entries)}
893 % elif diff_mode == 'sideside':
893 % elif diff_mode == 'sideside':
894 ${render_hunk_lines_sideside(filediff, hunk, use_comments=use_comments, inline_comments=inline_comments, active_pattern_entries=active_pattern_entries)}
894 ${render_hunk_lines_sideside(filediff, hunk, use_comments=use_comments, inline_comments=inline_comments, active_pattern_entries=active_pattern_entries)}
895 % else:
895 % else:
896 <tr class="cb-line">
896 <tr class="cb-line">
897 <td>unknown diff mode</td>
897 <td>unknown diff mode</td>
898 </tr>
898 </tr>
899 % endif
899 % endif
900 </%def>file changes
900 </%def>file changes
901
901
902
902
903 <%def name="render_add_comment_button(line_no='', f_path='')">
903 <%def name="render_add_comment_button(line_no='', f_path='')">
904 % if not c.rhodecode_user.is_default:
904 % if not c.rhodecode_user.is_default:
905 <button class="btn btn-small btn-primary cb-comment-box-opener" onclick="return Rhodecode.comments.createComment(this, '${f_path}', '${line_no}', null)">
905 <button class="btn btn-small btn-primary cb-comment-box-opener" onclick="return Rhodecode.comments.createComment(this, '${f_path}', '${line_no}', null)">
906 <span><i class="icon-comment"></i></span>
906 <span><i class="icon-comment"></i></span>
907 </button>
907 </button>
908 % endif
908 % endif
909 </%def>
909 </%def>
910
910
911 <%def name="render_diffset_menu(diffset, range_diff_on=None, commit=None, pull_request_menu=None)">
911 <%def name="render_diffset_menu(diffset, range_diff_on=None, commit=None, pull_request_menu=None)">
912 <% diffset_container_id = h.md5(diffset.target_ref) %>
912 <% diffset_container_id = h.md5(diffset.target_ref) %>
913
913
914 <div id="diff-file-sticky" class="diffset-menu clearinner">
914 <div id="diff-file-sticky" class="diffset-menu clearinner">
915 ## auto adjustable
915 ## auto adjustable
916 <div class="sidebar__inner">
916 <div class="sidebar__inner">
917 <div class="sidebar__bar">
917 <div class="sidebar__bar">
918 <div class="pull-right">
918 <div class="pull-right">
919
920 <div class="btn-group" style="margin-right: 5px;">
921 <a class="tooltip btn" onclick="scrollDown();return false" title="${_('Scroll to page bottom')}">
922 <i class="icon-arrow_down"></i>
923 </a>
924 <a class="tooltip btn" onclick="scrollUp();return false" title="${_('Scroll to page top')}">
925 <i class="icon-arrow_up"></i>
926 </a>
927 </div>
928
919 <div class="btn-group">
929 <div class="btn-group">
920 <a class="btn tooltip toggle-wide-diff" href="#toggle-wide-diff" onclick="toggleWideDiff(this); return false" title="${h.tooltip(_('Toggle wide diff'))}">
930 <a class="btn tooltip toggle-wide-diff" href="#toggle-wide-diff" onclick="toggleWideDiff(this); return false" title="${h.tooltip(_('Toggle wide diff'))}">
921 <i class="icon-wide-mode"></i>
931 <i class="icon-wide-mode"></i>
922 </a>
932 </a>
923 </div>
933 </div>
924 <div class="btn-group">
934 <div class="btn-group">
925
935
926 <a
936 <a
927 class="btn ${(c.user_session_attrs["diffmode"] == 'sideside' and 'btn-active')} tooltip"
937 class="btn ${(c.user_session_attrs["diffmode"] == 'sideside' and 'btn-active')} tooltip"
928 title="${h.tooltip(_('View diff as side by side'))}"
938 title="${h.tooltip(_('View diff as side by side'))}"
929 href="${h.current_route_path(request, diffmode='sideside')}">
939 href="${h.current_route_path(request, diffmode='sideside')}">
930 <span>${_('Side by Side')}</span>
940 <span>${_('Side by Side')}</span>
931 </a>
941 </a>
932
942
933 <a
943 <a
934 class="btn ${(c.user_session_attrs["diffmode"] == 'unified' and 'btn-active')} tooltip"
944 class="btn ${(c.user_session_attrs["diffmode"] == 'unified' and 'btn-active')} tooltip"
935 title="${h.tooltip(_('View diff as unified'))}" href="${h.current_route_path(request, diffmode='unified')}">
945 title="${h.tooltip(_('View diff as unified'))}" href="${h.current_route_path(request, diffmode='unified')}">
936 <span>${_('Unified')}</span>
946 <span>${_('Unified')}</span>
937 </a>
947 </a>
938
948
939 % if range_diff_on is True:
949 % if range_diff_on is True:
940 <a
950 <a
941 title="${_('Turn off: Show the diff as commit range')}"
951 title="${_('Turn off: Show the diff as commit range')}"
942 class="btn btn-primary"
952 class="btn btn-primary"
943 href="${h.current_route_path(request, **{"range-diff":"0"})}">
953 href="${h.current_route_path(request, **{"range-diff":"0"})}">
944 <span>${_('Range Diff')}</span>
954 <span>${_('Range Diff')}</span>
945 </a>
955 </a>
946 % elif range_diff_on is False:
956 % elif range_diff_on is False:
947 <a
957 <a
948 title="${_('Show the diff as commit range')}"
958 title="${_('Show the diff as commit range')}"
949 class="btn"
959 class="btn"
950 href="${h.current_route_path(request, **{"range-diff":"1"})}">
960 href="${h.current_route_path(request, **{"range-diff":"1"})}">
951 <span>${_('Range Diff')}</span>
961 <span>${_('Range Diff')}</span>
952 </a>
962 </a>
953 % endif
963 % endif
954 </div>
964 </div>
955 <div class="btn-group">
965 <div class="btn-group">
956
966
957 <details class="details-reset details-inline-block">
967 <details class="details-reset details-inline-block">
958 <summary class="noselect btn">
968 <summary class="noselect btn">
959 <i class="icon-options cursor-pointer" op="options"></i>
969 <i class="icon-options cursor-pointer" op="options"></i>
960 </summary>
970 </summary>
961
971
962 <div>
972 <div>
963 <details-menu class="details-dropdown" style="top: 35px;">
973 <details-menu class="details-dropdown" style="top: 35px;">
964
974
965 <div class="dropdown-item">
975 <div class="dropdown-item">
966 <div style="padding: 2px 0px">
976 <div style="padding: 2px 0px">
967 % if request.GET.get('ignorews', '') == '1':
977 % if request.GET.get('ignorews', '') == '1':
968 <a href="${h.current_route_path(request, ignorews=0)}">${_('Show whitespace changes')}</a>
978 <a href="${h.current_route_path(request, ignorews=0)}">${_('Show whitespace changes')}</a>
969 % else:
979 % else:
970 <a href="${h.current_route_path(request, ignorews=1)}">${_('Hide whitespace changes')}</a>
980 <a href="${h.current_route_path(request, ignorews=1)}">${_('Hide whitespace changes')}</a>
971 % endif
981 % endif
972 </div>
982 </div>
973 </div>
983 </div>
974
984
975 <div class="dropdown-item">
985 <div class="dropdown-item">
976 <div style="padding: 2px 0px">
986 <div style="padding: 2px 0px">
977 % if request.GET.get('fullcontext', '') == '1':
987 % if request.GET.get('fullcontext', '') == '1':
978 <a href="${h.current_route_path(request, fullcontext=0)}">${_('Hide full context diff')}</a>
988 <a href="${h.current_route_path(request, fullcontext=0)}">${_('Hide full context diff')}</a>
979 % else:
989 % else:
980 <a href="${h.current_route_path(request, fullcontext=1)}">${_('Show full context diff')}</a>
990 <a href="${h.current_route_path(request, fullcontext=1)}">${_('Show full context diff')}</a>
981 % endif
991 % endif
982 </div>
992 </div>
983 </div>
993 </div>
984
994
985 </details-menu>
995 </details-menu>
986 </div>
996 </div>
987 </details>
997 </details>
988
998
989 </div>
999 </div>
990 </div>
1000 </div>
991 <div class="pull-left">
1001 <div class="pull-left">
992 <div class="btn-group">
1002 <div class="btn-group">
993 <div class="pull-left">
1003 <div class="pull-left">
994 ${h.hidden('file_filter_{}'.format(diffset_container_id))}
1004 ${h.hidden('file_filter_{}'.format(diffset_container_id))}
995 </div>
1005 </div>
996
1006
997 </div>
1007 </div>
998 </div>
1008 </div>
999 </div>
1009 </div>
1000 <div class="fpath-placeholder pull-left">
1010 <div class="fpath-placeholder pull-left">
1001 <i class="icon-file-text"></i>
1011 <i class="icon-file-text"></i>
1002 <strong class="fpath-placeholder-text">
1012 <strong class="fpath-placeholder-text">
1003 Context file:
1013 Context file:
1004 </strong>
1014 </strong>
1005 </div>
1015 </div>
1006 <div class="pull-right noselect">
1016 <div class="pull-right noselect">
1007
1008 %if commit:
1017 %if commit:
1009 <span>
1018 <span>
1010 <code>${h.show_id(commit)}</code>
1019 <code>${h.show_id(commit)}</code>
1011 </span>
1020 </span>
1012 %elif pull_request_menu and pull_request_menu.get('pull_request'):
1021 %elif pull_request_menu and pull_request_menu.get('pull_request'):
1013 <span>
1022 <span>
1014 <code>!${pull_request_menu['pull_request'].pull_request_id}</code>
1023 <code>!${pull_request_menu['pull_request'].pull_request_id}</code>
1015 </span>
1024 </span>
1016 %endif
1025 %endif
1017 % if commit or pull_request_menu:
1026 % if commit or pull_request_menu:
1018 <span class="tooltip" title="Navigate to previous or next change inside files." id="diff_nav">Loading diff...:</span>
1027 <span class="tooltip" title="Navigate to previous or next change inside files." id="diff_nav">Loading diff...:</span>
1019 <span class="cursor-pointer" onclick="scrollToPrevChunk(); return false">
1028 <span class="cursor-pointer" onclick="scrollToPrevChunk(); return false">
1020 <i class="icon-angle-up"></i>
1029 <i class="icon-angle-up"></i>
1021 </span>
1030 </span>
1022 <span class="cursor-pointer" onclick="scrollToNextChunk(); return false">
1031 <span class="cursor-pointer" onclick="scrollToNextChunk(); return false">
1023 <i class="icon-angle-down"></i>
1032 <i class="icon-angle-down"></i>
1024 </span>
1033 </span>
1025 % endif
1034 % endif
1026 </div>
1035 </div>
1027 <div class="sidebar_inner_shadow"></div>
1036 <div class="sidebar_inner_shadow"></div>
1028 </div>
1037 </div>
1029 </div>
1038 </div>
1030
1039
1031 % if diffset:
1040 % if diffset:
1032 %if diffset.limited_diff:
1041 %if diffset.limited_diff:
1033 <% file_placeholder = _ungettext('%(num)s file changed', '%(num)s files changed', diffset.changed_files) % {'num': diffset.changed_files} %>
1042 <% file_placeholder = _ungettext('%(num)s file changed', '%(num)s files changed', diffset.changed_files) % {'num': diffset.changed_files} %>
1034 %else:
1043 %else:
1035 <% file_placeholder = h.literal(_ungettext('%(num)s file changed: <span class="op-added">%(linesadd)s inserted</span>, <span class="op-deleted">%(linesdel)s deleted</span>', '%(num)s files changed: <span class="op-added">%(linesadd)s inserted</span>, <span class="op-deleted">%(linesdel)s deleted</span>',
1044 <% file_placeholder = h.literal(_ungettext('%(num)s file changed: <span class="op-added">%(linesadd)s inserted</span>, <span class="op-deleted">%(linesdel)s deleted</span>', '%(num)s files changed: <span class="op-added">%(linesadd)s inserted</span>, <span class="op-deleted">%(linesdel)s deleted</span>',
1036 diffset.changed_files) % {'num': diffset.changed_files, 'linesadd': diffset.lines_added, 'linesdel': diffset.lines_deleted}) %>
1045 diffset.changed_files) % {'num': diffset.changed_files, 'linesadd': diffset.lines_added, 'linesdel': diffset.lines_deleted}) %>
1037
1046
1038 %endif
1047 %endif
1039 ## case on range-diff placeholder needs to be updated
1048 ## case on range-diff placeholder needs to be updated
1040 % if range_diff_on is True:
1049 % if range_diff_on is True:
1041 <% file_placeholder = _('Disabled on range diff') %>
1050 <% file_placeholder = _('Disabled on range diff') %>
1042 % endif
1051 % endif
1043
1052
1044 <script type="text/javascript">
1053 <script type="text/javascript">
1045 var feedFilesOptions = function (query, initialData) {
1054 var feedFilesOptions = function (query, initialData) {
1046 var data = {results: []};
1055 var data = {results: []};
1047 var isQuery = typeof query.term !== 'undefined';
1056 var isQuery = typeof query.term !== 'undefined';
1048
1057
1049 var section = _gettext('Changed files');
1058 var section = _gettext('Changed files');
1050 var filteredData = [];
1059 var filteredData = [];
1051
1060
1052 //filter results
1061 //filter results
1053 $.each(initialData.results, function (idx, value) {
1062 $.each(initialData.results, function (idx, value) {
1054
1063
1055 if (!isQuery || query.term.length === 0 || value.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0) {
1064 if (!isQuery || query.term.length === 0 || value.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0) {
1056 filteredData.push({
1065 filteredData.push({
1057 'id': this.id,
1066 'id': this.id,
1058 'text': this.text,
1067 'text': this.text,
1059 "ops": this.ops,
1068 "ops": this.ops,
1060 })
1069 })
1061 }
1070 }
1062
1071
1063 });
1072 });
1064
1073
1065 data.results = filteredData;
1074 data.results = filteredData;
1066
1075
1067 query.callback(data);
1076 query.callback(data);
1068 };
1077 };
1069
1078
1070 var selectionFormatter = function(data, escapeMarkup) {
1079 var selectionFormatter = function(data, escapeMarkup) {
1071 var container = '<div class="filelist" style="padding-right:100px">{0}</div>';
1080 var container = '<div class="filelist" style="padding-right:100px">{0}</div>';
1072 var tmpl = '<div><strong>{0}</strong></div>'.format(escapeMarkup(data['text']));
1081 var tmpl = '<div><strong>{0}</strong></div>'.format(escapeMarkup(data['text']));
1073 var pill = '<div class="pill-group" style="position: absolute; top:7px; right: 0">' +
1082 var pill = '<div class="pill-group" style="position: absolute; top:7px; right: 0">' +
1074 '<span class="pill" op="added">{0}</span>' +
1083 '<span class="pill" op="added">{0}</span>' +
1075 '<span class="pill" op="deleted">{1}</span>' +
1084 '<span class="pill" op="deleted">{1}</span>' +
1076 '</div>'
1085 '</div>'
1077 ;
1086 ;
1078 var added = data['ops']['added'];
1087 var added = data['ops']['added'];
1079 if (added === 0) {
1088 if (added === 0) {
1080 // don't show +0
1089 // don't show +0
1081 added = 0;
1090 added = 0;
1082 } else {
1091 } else {
1083 added = '+' + added;
1092 added = '+' + added;
1084 }
1093 }
1085
1094
1086 var deleted = -1*data['ops']['deleted'];
1095 var deleted = -1*data['ops']['deleted'];
1087
1096
1088 tmpl += pill.format(added, deleted);
1097 tmpl += pill.format(added, deleted);
1089 return container.format(tmpl);
1098 return container.format(tmpl);
1090 };
1099 };
1091 var formatFileResult = function(result, container, query, escapeMarkup) {
1100 var formatFileResult = function(result, container, query, escapeMarkup) {
1092 return selectionFormatter(result, escapeMarkup);
1101 return selectionFormatter(result, escapeMarkup);
1093 };
1102 };
1094
1103
1095 var formatSelection = function (data, container) {
1104 var formatSelection = function (data, container) {
1096 return '${file_placeholder}'
1105 return '${file_placeholder}'
1097 };
1106 };
1098
1107
1099 if (window.preloadFileFilterData === undefined) {
1108 if (window.preloadFileFilterData === undefined) {
1100 window.preloadFileFilterData = {}
1109 window.preloadFileFilterData = {}
1101 }
1110 }
1102
1111
1103 preloadFileFilterData["${diffset_container_id}"] = {
1112 preloadFileFilterData["${diffset_container_id}"] = {
1104 results: [
1113 results: [
1105 % for filediff in diffset.files:
1114 % for filediff in diffset.files:
1106 {id:"a_${h.FID(filediff.raw_id, filediff.patch['filename'])}",
1115 {id:"a_${h.FID(filediff.raw_id, filediff.patch['filename'])}",
1107 text:"${filediff.patch['filename']}",
1116 text:"${filediff.patch['filename']}",
1108 ops:${h.json.dumps(filediff.patch['stats'])|n}}${('' if loop.last else ',')}
1117 ops:${h.json.dumps(filediff.patch['stats'])|n}}${('' if loop.last else ',')}
1109 % endfor
1118 % endfor
1110 ]
1119 ]
1111 };
1120 };
1112
1121
1113 var diffFileFilterId = "#file_filter_" + "${diffset_container_id}";
1122 var diffFileFilterId = "#file_filter_" + "${diffset_container_id}";
1114 var diffFileFilter = $(diffFileFilterId).select2({
1123 var diffFileFilter = $(diffFileFilterId).select2({
1115 'dropdownAutoWidth': true,
1124 'dropdownAutoWidth': true,
1116 'width': 'auto',
1125 'width': 'auto',
1117
1126
1118 containerCssClass: "drop-menu",
1127 containerCssClass: "drop-menu",
1119 dropdownCssClass: "drop-menu-dropdown",
1128 dropdownCssClass: "drop-menu-dropdown",
1120 data: preloadFileFilterData["${diffset_container_id}"],
1129 data: preloadFileFilterData["${diffset_container_id}"],
1121 query: function(query) {
1130 query: function(query) {
1122 feedFilesOptions(query, preloadFileFilterData["${diffset_container_id}"]);
1131 feedFilesOptions(query, preloadFileFilterData["${diffset_container_id}"]);
1123 },
1132 },
1124 initSelection: function(element, callback) {
1133 initSelection: function(element, callback) {
1125 callback({'init': true});
1134 callback({'init': true});
1126 },
1135 },
1127 formatResult: formatFileResult,
1136 formatResult: formatFileResult,
1128 formatSelection: formatSelection
1137 formatSelection: formatSelection
1129 });
1138 });
1130
1139
1131 % if range_diff_on is True:
1140 % if range_diff_on is True:
1132 diffFileFilter.select2("enable", false);
1141 diffFileFilter.select2("enable", false);
1133 % endif
1142 % endif
1134
1143
1135 $(diffFileFilterId).on('select2-selecting', function (e) {
1144 $(diffFileFilterId).on('select2-selecting', function (e) {
1136 var idSelector = e.choice.id;
1145 var idSelector = e.choice.id;
1137
1146
1138 // expand the container if we quick-select the field
1147 // expand the container if we quick-select the field
1139 $('#'+idSelector).next().prop('checked', false);
1148 $('#'+idSelector).next().prop('checked', false);
1140 // hide the mast as we later do preventDefault()
1149 // hide the mast as we later do preventDefault()
1141 $("#select2-drop-mask").click();
1150 $("#select2-drop-mask").click();
1142
1151
1143 window.location.hash = '#'+idSelector;
1152 window.location.hash = '#'+idSelector;
1144 updateSticky();
1153 updateSticky();
1145
1154
1146 e.preventDefault();
1155 e.preventDefault();
1147 });
1156 });
1148
1157
1149 diffNavText = 'diff navigation:'
1158 diffNavText = 'diff navigation:'
1150
1159
1151 getCurrentChunk = function () {
1160 getCurrentChunk = function () {
1152
1161
1153 var chunksAll = $('.nav-chunk').filter(function () {
1162 var chunksAll = $('.nav-chunk').filter(function () {
1154 return $(this).parents('.filediff').prev().get(0).checked !== true
1163 return $(this).parents('.filediff').prev().get(0).checked !== true
1155 })
1164 })
1156 var chunkSelected = $('.nav-chunk.selected');
1165 var chunkSelected = $('.nav-chunk.selected');
1157 var initial = false;
1166 var initial = false;
1158
1167
1159 if (chunkSelected.length === 0) {
1168 if (chunkSelected.length === 0) {
1160 // no initial chunk selected, we pick first
1169 // no initial chunk selected, we pick first
1161 chunkSelected = $(chunksAll.get(0));
1170 chunkSelected = $(chunksAll.get(0));
1162 var initial = true;
1171 var initial = true;
1163 }
1172 }
1164
1173
1165 return {
1174 return {
1166 'all': chunksAll,
1175 'all': chunksAll,
1167 'selected': chunkSelected,
1176 'selected': chunkSelected,
1168 'initial': initial,
1177 'initial': initial,
1169 }
1178 }
1170 }
1179 }
1171
1180
1172 animateDiffNavText = function () {
1181 animateDiffNavText = function () {
1173 var $diffNav = $('#diff_nav')
1182 var $diffNav = $('#diff_nav')
1174
1183
1175 var callback = function () {
1184 var callback = function () {
1176 $diffNav.animate({'opacity': 1.00}, 200)
1185 $diffNav.animate({'opacity': 1.00}, 200)
1177 };
1186 };
1178 $diffNav.animate({'opacity': 0.15}, 200, callback);
1187 $diffNav.animate({'opacity': 0.15}, 200, callback);
1179 }
1188 }
1180
1189
1181 scrollToChunk = function (moveBy) {
1190 scrollToChunk = function (moveBy) {
1182 var chunk = getCurrentChunk();
1191 var chunk = getCurrentChunk();
1183 var all = chunk.all
1192 var all = chunk.all
1184 var selected = chunk.selected
1193 var selected = chunk.selected
1185
1194
1186 var curPos = all.index(selected);
1195 var curPos = all.index(selected);
1187 var newPos = curPos;
1196 var newPos = curPos;
1188 if (!chunk.initial) {
1197 if (!chunk.initial) {
1189 var newPos = curPos + moveBy;
1198 var newPos = curPos + moveBy;
1190 }
1199 }
1191
1200
1192 var curElem = all.get(newPos);
1201 var curElem = all.get(newPos);
1193
1202
1194 if (curElem === undefined) {
1203 if (curElem === undefined) {
1195 // end or back
1204 // end or back
1196 $('#diff_nav').html('no next diff element:')
1205 $('#diff_nav').html('no next diff element:')
1197 animateDiffNavText()
1206 animateDiffNavText()
1198 return
1207 return
1199 } else if (newPos < 0) {
1208 } else if (newPos < 0) {
1200 $('#diff_nav').html('no previous diff element:')
1209 $('#diff_nav').html('no previous diff element:')
1201 animateDiffNavText()
1210 animateDiffNavText()
1202 return
1211 return
1203 } else {
1212 } else {
1204 $('#diff_nav').html(diffNavText)
1213 $('#diff_nav').html(diffNavText)
1205 }
1214 }
1206
1215
1207 curElem = $(curElem)
1216 curElem = $(curElem)
1208 var offset = 100;
1217 var offset = 100;
1209 $(window).scrollTop(curElem.position().top - offset);
1218 $(window).scrollTop(curElem.position().top - offset);
1210
1219
1211 //clear selection
1220 //clear selection
1212 all.removeClass('selected')
1221 all.removeClass('selected')
1213 curElem.addClass('selected')
1222 curElem.addClass('selected')
1214 }
1223 }
1215
1224
1216 scrollToPrevChunk = function () {
1225 scrollToPrevChunk = function () {
1217 scrollToChunk(-1)
1226 scrollToChunk(-1)
1218 }
1227 }
1219 scrollToNextChunk = function () {
1228 scrollToNextChunk = function () {
1220 scrollToChunk(1)
1229 scrollToChunk(1)
1221 }
1230 }
1222
1231
1223 </script>
1232 </script>
1224 % endif
1233 % endif
1225
1234
1226 <script type="text/javascript">
1235 <script type="text/javascript">
1227 $('#diff_nav').html('loading diff...') // wait until whole page is loaded
1236 $('#diff_nav').html('loading diff...') // wait until whole page is loaded
1228
1237
1229 $(document).ready(function () {
1238 $(document).ready(function () {
1230
1239
1231 var contextPrefix = _gettext('Context file: ');
1240 var contextPrefix = _gettext('Context file: ');
1232 ## sticky sidebar
1241 ## sticky sidebar
1233 var sidebarElement = document.getElementById('diff-file-sticky');
1242 var sidebarElement = document.getElementById('diff-file-sticky');
1234 sidebar = new StickySidebar(sidebarElement, {
1243 sidebar = new StickySidebar(sidebarElement, {
1235 topSpacing: 0,
1244 topSpacing: 0,
1236 bottomSpacing: 0,
1245 bottomSpacing: 0,
1237 innerWrapperSelector: '.sidebar__inner'
1246 innerWrapperSelector: '.sidebar__inner'
1238 });
1247 });
1239 sidebarElement.addEventListener('affixed.static.stickySidebar', function () {
1248 sidebarElement.addEventListener('affixed.static.stickySidebar', function () {
1240 // reset our file so it's not holding new value
1249 // reset our file so it's not holding new value
1241 $('.fpath-placeholder-text').html(contextPrefix + ' - ')
1250 $('.fpath-placeholder-text').html(contextPrefix + ' - ')
1242 });
1251 });
1243
1252
1244 updateSticky = function () {
1253 updateSticky = function () {
1245 sidebar.updateSticky();
1254 sidebar.updateSticky();
1246 Waypoint.refreshAll();
1255 Waypoint.refreshAll();
1247 };
1256 };
1248
1257
1249 var animateText = function (fPath, anchorId) {
1258 var animateText = function (fPath, anchorId) {
1250 fPath = Select2.util.escapeMarkup(fPath);
1259 fPath = Select2.util.escapeMarkup(fPath);
1251 $('.fpath-placeholder-text').html(contextPrefix + '<a href="#a_' + anchorId + '">' + fPath + '</a>')
1260 $('.fpath-placeholder-text').html(contextPrefix + '<a href="#a_' + anchorId + '">' + fPath + '</a>')
1252 };
1261 };
1253
1262
1254 ## dynamic file waypoints
1263 ## dynamic file waypoints
1255 var setFPathInfo = function(fPath, anchorId){
1264 var setFPathInfo = function(fPath, anchorId){
1256 animateText(fPath, anchorId)
1265 animateText(fPath, anchorId)
1257 };
1266 };
1258
1267
1259 var codeBlock = $('.filediff');
1268 var codeBlock = $('.filediff');
1260
1269
1261 // forward waypoint
1270 // forward waypoint
1262 codeBlock.waypoint(
1271 codeBlock.waypoint(
1263 function(direction) {
1272 function(direction) {
1264 if (direction === "down"){
1273 if (direction === "down"){
1265 setFPathInfo($(this.element).data('fPath'), $(this.element).data('anchorId'))
1274 setFPathInfo($(this.element).data('fPath'), $(this.element).data('anchorId'))
1266 }
1275 }
1267 }, {
1276 }, {
1268 offset: function () {
1277 offset: function () {
1269 return 70;
1278 return 70;
1270 },
1279 },
1271 context: '.fpath-placeholder'
1280 context: '.fpath-placeholder'
1272 }
1281 }
1273 );
1282 );
1274
1283
1275 // backward waypoint
1284 // backward waypoint
1276 codeBlock.waypoint(
1285 codeBlock.waypoint(
1277 function(direction) {
1286 function(direction) {
1278 if (direction === "up"){
1287 if (direction === "up"){
1279 setFPathInfo($(this.element).data('fPath'), $(this.element).data('anchorId'))
1288 setFPathInfo($(this.element).data('fPath'), $(this.element).data('anchorId'))
1280 }
1289 }
1281 }, {
1290 }, {
1282 offset: function () {
1291 offset: function () {
1283 return -this.element.clientHeight + 90;
1292 return -this.element.clientHeight + 90;
1284 },
1293 },
1285 context: '.fpath-placeholder'
1294 context: '.fpath-placeholder'
1286 }
1295 }
1287 );
1296 );
1288
1297
1289 toggleWideDiff = function (el) {
1298 toggleWideDiff = function (el) {
1290 updateSticky();
1299 updateSticky();
1291 var wide = Rhodecode.comments.toggleWideMode(this);
1300 var wide = Rhodecode.comments.toggleWideMode(this);
1292 storeUserSessionAttr('rc_user_session_attr.wide_diff_mode', wide);
1301 storeUserSessionAttr('rc_user_session_attr.wide_diff_mode', wide);
1293 if (wide === true) {
1302 if (wide === true) {
1294 $(el).addClass('btn-active');
1303 $(el).addClass('btn-active');
1295 } else {
1304 } else {
1296 $(el).removeClass('btn-active');
1305 $(el).removeClass('btn-active');
1297 }
1306 }
1298 return null;
1307 return null;
1299 };
1308 };
1300
1309
1301 toggleExpand = function (el, diffsetEl) {
1310 toggleExpand = function (el, diffsetEl) {
1302 var el = $(el);
1311 var el = $(el);
1303 if (el.hasClass('collapsed')) {
1312 if (el.hasClass('collapsed')) {
1304 $('.filediff-collapse-state.collapse-{0}'.format(diffsetEl)).prop('checked', false);
1313 $('.filediff-collapse-state.collapse-{0}'.format(diffsetEl)).prop('checked', false);
1305 el.removeClass('collapsed');
1314 el.removeClass('collapsed');
1306 el.html(
1315 el.html(
1307 '<i class="icon-minus-squared-alt icon-no-margin"></i>' +
1316 '<i class="icon-minus-squared-alt icon-no-margin"></i>' +
1308 _gettext('Collapse all files'));
1317 _gettext('Collapse all files'));
1309 }
1318 }
1310 else {
1319 else {
1311 $('.filediff-collapse-state.collapse-{0}'.format(diffsetEl)).prop('checked', true);
1320 $('.filediff-collapse-state.collapse-{0}'.format(diffsetEl)).prop('checked', true);
1312 el.addClass('collapsed');
1321 el.addClass('collapsed');
1313 el.html(
1322 el.html(
1314 '<i class="icon-plus-squared-alt icon-no-margin"></i>' +
1323 '<i class="icon-plus-squared-alt icon-no-margin"></i>' +
1315 _gettext('Expand all files'));
1324 _gettext('Expand all files'));
1316 }
1325 }
1317 updateSticky()
1326 updateSticky()
1318 };
1327 };
1319
1328
1320 toggleCommitExpand = function (el) {
1329 toggleCommitExpand = function (el) {
1321 var $el = $(el);
1330 var $el = $(el);
1322 var commits = $el.data('toggleCommitsCnt');
1331 var commits = $el.data('toggleCommitsCnt');
1323 var collapseMsg = _ngettext('Collapse {0} commit', 'Collapse {0} commits', commits).format(commits);
1332 var collapseMsg = _ngettext('Collapse {0} commit', 'Collapse {0} commits', commits).format(commits);
1324 var expandMsg = _ngettext('Expand {0} commit', 'Expand {0} commits', commits).format(commits);
1333 var expandMsg = _ngettext('Expand {0} commit', 'Expand {0} commits', commits).format(commits);
1325
1334
1326 if ($el.hasClass('collapsed')) {
1335 if ($el.hasClass('collapsed')) {
1327 $('.compare_select').show();
1336 $('.compare_select').show();
1328 $('.compare_select_hidden').hide();
1337 $('.compare_select_hidden').hide();
1329
1338
1330 $el.removeClass('collapsed');
1339 $el.removeClass('collapsed');
1331 $el.html(
1340 $el.html(
1332 '<i class="icon-minus-squared-alt icon-no-margin"></i>' +
1341 '<i class="icon-minus-squared-alt icon-no-margin"></i>' +
1333 collapseMsg);
1342 collapseMsg);
1334 }
1343 }
1335 else {
1344 else {
1336 $('.compare_select').hide();
1345 $('.compare_select').hide();
1337 $('.compare_select_hidden').show();
1346 $('.compare_select_hidden').show();
1338 $el.addClass('collapsed');
1347 $el.addClass('collapsed');
1339 $el.html(
1348 $el.html(
1340 '<i class="icon-plus-squared-alt icon-no-margin"></i>' +
1349 '<i class="icon-plus-squared-alt icon-no-margin"></i>' +
1341 expandMsg);
1350 expandMsg);
1342 }
1351 }
1343 updateSticky();
1352 updateSticky();
1344 };
1353 };
1345
1354
1346 // get stored diff mode and pre-enable it
1355 // get stored diff mode and pre-enable it
1347 if (templateContext.session_attrs.wide_diff_mode === "true") {
1356 if (templateContext.session_attrs.wide_diff_mode === "true") {
1348 Rhodecode.comments.toggleWideMode(null);
1357 Rhodecode.comments.toggleWideMode(null);
1349 $('.toggle-wide-diff').addClass('btn-active');
1358 $('.toggle-wide-diff').addClass('btn-active');
1350 updateSticky();
1359 updateSticky();
1351 }
1360 }
1352
1361
1353 // DIFF NAV //
1362 // DIFF NAV //
1354
1363
1355 // element to detect scroll direction of
1364 // element to detect scroll direction of
1356 var $window = $(window);
1365 var $window = $(window);
1357
1366
1358 // initialize last scroll position
1367 // initialize last scroll position
1359 var lastScrollY = $window.scrollTop();
1368 var lastScrollY = $window.scrollTop();
1360
1369
1361 $window.on('resize scrollstop', {latency: 350}, function () {
1370 $window.on('resize scrollstop', {latency: 350}, function () {
1362 var visibleChunks = $('.nav-chunk').withinviewport({top: 75});
1371 var visibleChunks = $('.nav-chunk').withinviewport({top: 75});
1363
1372
1364 // get current scroll position
1373 // get current scroll position
1365 var currentScrollY = $window.scrollTop();
1374 var currentScrollY = $window.scrollTop();
1366
1375
1367 // determine current scroll direction
1376 // determine current scroll direction
1368 if (currentScrollY > lastScrollY) {
1377 if (currentScrollY > lastScrollY) {
1369 var y = 'down'
1378 var y = 'down'
1370 } else if (currentScrollY !== lastScrollY) {
1379 } else if (currentScrollY !== lastScrollY) {
1371 var y = 'up';
1380 var y = 'up';
1372 }
1381 }
1373
1382
1374 var pos = -1; // by default we use last element in viewport
1383 var pos = -1; // by default we use last element in viewport
1375 if (y === 'down') {
1384 if (y === 'down') {
1376 pos = -1;
1385 pos = -1;
1377 } else if (y === 'up') {
1386 } else if (y === 'up') {
1378 pos = 0;
1387 pos = 0;
1379 }
1388 }
1380
1389
1381 if (visibleChunks.length > 0) {
1390 if (visibleChunks.length > 0) {
1382 $('.nav-chunk').removeClass('selected');
1391 $('.nav-chunk').removeClass('selected');
1383 $(visibleChunks.get(pos)).addClass('selected');
1392 $(visibleChunks.get(pos)).addClass('selected');
1384 }
1393 }
1385
1394
1386 // update last scroll position to current position
1395 // update last scroll position to current position
1387 lastScrollY = currentScrollY;
1396 lastScrollY = currentScrollY;
1388
1397
1389 });
1398 });
1390 $('#diff_nav').html(diffNavText);
1399 $('#diff_nav').html(diffNavText);
1391
1400
1392 });
1401 });
1393 </script>
1402 </script>
1394
1403
1395 </%def>
1404 </%def>
@@ -1,1051 +1,1052 b''
1 <%inherit file="/base/base.mako"/>
1 <%inherit file="/base/base.mako"/>
2 <%namespace name="base" file="/base/base.mako"/>
2 <%namespace name="base" file="/base/base.mako"/>
3 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
3 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
4 <%namespace name="sidebar" file="/base/sidebar.mako"/>
4 <%namespace name="sidebar" file="/base/sidebar.mako"/>
5
5
6
6
7 <%def name="title()">
7 <%def name="title()">
8 ${_('{} Pull Request !{}').format(c.repo_name, c.pull_request.pull_request_id)}
8 ${_('{} Pull Request !{}').format(c.repo_name, c.pull_request.pull_request_id)}
9 %if c.rhodecode_name:
9 %if c.rhodecode_name:
10 &middot; ${h.branding(c.rhodecode_name)}
10 &middot; ${h.branding(c.rhodecode_name)}
11 %endif
11 %endif
12 </%def>
12 </%def>
13
13
14 <%def name="breadcrumbs_links()">
14 <%def name="breadcrumbs_links()">
15
15
16 </%def>
16 </%def>
17
17
18 <%def name="menu_bar_nav()">
18 <%def name="menu_bar_nav()">
19 ${self.menu_items(active='repositories')}
19 ${self.menu_items(active='repositories')}
20 </%def>
20 </%def>
21
21
22 <%def name="menu_bar_subnav()">
22 <%def name="menu_bar_subnav()">
23 ${self.repo_menu(active='showpullrequest')}
23 ${self.repo_menu(active='showpullrequest')}
24 </%def>
24 </%def>
25
25
26
26
27 <%def name="main()">
27 <%def name="main()">
28 ## Container to gather extracted Tickets
28 ## Container to gather extracted Tickets
29 <%
29 <%
30 c.referenced_commit_issues = []
30 c.referenced_commit_issues = []
31 c.referenced_desc_issues = []
31 c.referenced_desc_issues = []
32 %>
32 %>
33
33
34 <script type="text/javascript">
34 <script type="text/javascript">
35 templateContext.pull_request_data.pull_request_id = ${c.pull_request.pull_request_id};
35 templateContext.pull_request_data.pull_request_id = ${c.pull_request.pull_request_id};
36 templateContext.pull_request_data.pull_request_version = '${request.GET.get('version', '')}';
36 templateContext.pull_request_data.pull_request_version = '${request.GET.get('version', '')}';
37 </script>
37 </script>
38
38
39 <div class="box">
39 <div class="box">
40
40
41 <div class="box pr-summary">
41 <div class="box pr-summary">
42
42
43 <div class="summary-details block-left">
43 <div class="summary-details block-left">
44 <div id="pr-title">
44 <div id="pr-title">
45 % if c.pull_request.is_closed():
45 % if c.pull_request.is_closed():
46 <span class="pr-title-closed-tag tag">${_('Closed')}</span>
46 <span class="pr-title-closed-tag tag">${_('Closed')}</span>
47 % endif
47 % endif
48 <input class="pr-title-input large disabled" disabled="disabled" name="pullrequest_title" type="text" value="${c.pull_request.title}">
48 <input class="pr-title-input large disabled" disabled="disabled" name="pullrequest_title" type="text" value="${c.pull_request.title}">
49 </div>
49 </div>
50 <div id="pr-title-edit" class="input" style="display: none;">
50 <div id="pr-title-edit" class="input" style="display: none;">
51 <input class="pr-title-input large" id="pr-title-input" name="pullrequest_title" type="text" value="${c.pull_request.title}">
51 <input class="pr-title-input large" id="pr-title-input" name="pullrequest_title" type="text" value="${c.pull_request.title}">
52 </div>
52 </div>
53
53
54 <% summary = lambda n:{False:'summary-short'}.get(n) %>
54 <% summary = lambda n:{False:'summary-short'}.get(n) %>
55 <div class="pr-details-title">
55 <div class="pr-details-title">
56 <div class="pull-left">
56 <div class="pull-left">
57 <a href="${h.route_path('pull_requests_global', pull_request_id=c.pull_request.pull_request_id)}">${_('Pull request !{}').format(c.pull_request.pull_request_id)}</a>
57 <a href="${h.route_path('pull_requests_global', pull_request_id=c.pull_request.pull_request_id)}">${_('Pull request !{}').format(c.pull_request.pull_request_id)}</a>
58 ${_('Created on')}
58 ${_('Created on')}
59 <span class="tooltip" title="${_('Last updated on')} ${h.format_date(c.pull_request.updated_on)}">${h.format_date(c.pull_request.created_on)},</span>
59 <span class="tooltip" title="${_('Last updated on')} ${h.format_date(c.pull_request.updated_on)}">${h.format_date(c.pull_request.created_on)},</span>
60 <span class="pr-details-title-author-pref">${_('by')}</span>
60 <span class="pr-details-title-author-pref">${_('by')}</span>
61 </div>
61 </div>
62
62
63 <div class="pull-left">
63 <div class="pull-left">
64 ${self.gravatar_with_user(c.pull_request.author.email, 16, tooltip=True)}
64 ${self.gravatar_with_user(c.pull_request.author.email, 16, tooltip=True)}
65 </div>
65 </div>
66
66
67 %if c.allowed_to_update:
67 %if c.allowed_to_update:
68 <div class="pull-right">
68 <div class="pull-right">
69 <div id="edit_pull_request" class="action_button pr-save" style="display: none;">${_('Update title & description')}</div>
69 <div id="edit_pull_request" class="action_button pr-save" style="display: none;">${_('Update title & description')}</div>
70 <div id="delete_pullrequest" class="action_button pr-save ${('' if c.allowed_to_delete else 'disabled' )}" style="display: none;">
70 <div id="delete_pullrequest" class="action_button pr-save ${('' if c.allowed_to_delete else 'disabled' )}" style="display: none;">
71 % if c.allowed_to_delete:
71 % if c.allowed_to_delete:
72 ${h.secure_form(h.route_path('pullrequest_delete', repo_name=c.pull_request.target_repo.repo_name, pull_request_id=c.pull_request.pull_request_id), request=request)}
72 ${h.secure_form(h.route_path('pullrequest_delete', repo_name=c.pull_request.target_repo.repo_name, pull_request_id=c.pull_request.pull_request_id), request=request)}
73 <input class="btn btn-link btn-danger no-margin" id="remove_${c.pull_request.pull_request_id}" name="remove_${c.pull_request.pull_request_id}"
73 <input class="btn btn-link btn-danger no-margin" id="remove_${c.pull_request.pull_request_id}" name="remove_${c.pull_request.pull_request_id}"
74 onclick="submitConfirm(event, this, _gettext('Confirm to delete this pull request'), _gettext('Delete'), '${'!{}'.format(c.pull_request.pull_request_id)}')"
74 onclick="submitConfirm(event, this, _gettext('Confirm to delete this pull request'), _gettext('Delete'), '${'!{}'.format(c.pull_request.pull_request_id)}')"
75 type="submit" value="${_('Delete pull request')}">
75 type="submit" value="${_('Delete pull request')}">
76 ${h.end_form()}
76 ${h.end_form()}
77 % else:
77 % else:
78 <span class="tooltip" title="${_('Not allowed to delete this pull request')}">${_('Delete pull request')}</span>
78 <span class="tooltip" title="${_('Not allowed to delete this pull request')}">${_('Delete pull request')}</span>
79 % endif
79 % endif
80 </div>
80 </div>
81 <div id="open_edit_pullrequest" class="action_button">${_('Edit')}</div>
81 <div id="open_edit_pullrequest" class="action_button">${_('Edit')}</div>
82 <div id="close_edit_pullrequest" class="action_button" style="display: none;">${_('Cancel')}</div>
82 <div id="close_edit_pullrequest" class="action_button" style="display: none;">${_('Cancel')}</div>
83 </div>
83 </div>
84
84
85 %endif
85 %endif
86 </div>
86 </div>
87
87
88 <div id="pr-desc" class="input" title="${_('Rendered using {} renderer').format(c.renderer)}">
88 <div id="pr-desc" class="input" title="${_('Rendered using {} renderer').format(c.renderer)}">
89 ${h.render(c.pull_request.description, renderer=c.renderer, repo_name=c.repo_name, issues_container=c.referenced_desc_issues)}
89 ${h.render(c.pull_request.description, renderer=c.renderer, repo_name=c.repo_name, issues_container=c.referenced_desc_issues)}
90 </div>
90 </div>
91
91
92 <div id="pr-desc-edit" class="input textarea" style="display: none;">
92 <div id="pr-desc-edit" class="input textarea" style="display: none;">
93 <input id="pr-renderer-input" type="hidden" name="description_renderer" value="${c.visual.default_renderer}">
93 <input id="pr-renderer-input" type="hidden" name="description_renderer" value="${c.visual.default_renderer}">
94 ${dt.markup_form('pr-description-input', form_text=c.pull_request.description)}
94 ${dt.markup_form('pr-description-input', form_text=c.pull_request.description)}
95 </div>
95 </div>
96
96
97 <div id="summary" class="fields pr-details-content">
97 <div id="summary" class="fields pr-details-content">
98
98
99 ## source
99 ## source
100 <div class="field">
100 <div class="field">
101 <div class="label-pr-detail">
101 <div class="label-pr-detail">
102 <label>${_('Commit flow')}:</label>
102 <label>${_('Commit flow')}:</label>
103 </div>
103 </div>
104 <div class="input">
104 <div class="input">
105 <div class="pr-commit-flow">
105 <div class="pr-commit-flow">
106 ## Source
106 ## Source
107 %if c.pull_request.source_ref_parts.type == 'branch':
107 %if c.pull_request.source_ref_parts.type == 'branch':
108 <a href="${h.route_path('repo_commits', repo_name=c.pull_request.source_repo.repo_name, _query=dict(branch=c.pull_request.source_ref_parts.name))}"><code class="pr-source-info">${c.pull_request.source_ref_parts.type}:${c.pull_request.source_ref_parts.name}</code></a>
108 <a href="${h.route_path('repo_commits', repo_name=c.pull_request.source_repo.repo_name, _query=dict(branch=c.pull_request.source_ref_parts.name))}"><code class="pr-source-info">${c.pull_request.source_ref_parts.type}:${c.pull_request.source_ref_parts.name}</code></a>
109 %else:
109 %else:
110 <code class="pr-source-info">${'{}:{}'.format(c.pull_request.source_ref_parts.type, c.pull_request.source_ref_parts.name)}</code>
110 <code class="pr-source-info">${'{}:{}'.format(c.pull_request.source_ref_parts.type, c.pull_request.source_ref_parts.name)}</code>
111 %endif
111 %endif
112 ${_('of')} <a href="${h.route_path('repo_summary', repo_name=c.pull_request.source_repo.repo_name)}">${c.pull_request.source_repo.repo_name}</a>
112 ${_('of')} <a href="${h.route_path('repo_summary', repo_name=c.pull_request.source_repo.repo_name)}">${c.pull_request.source_repo.repo_name}</a>
113 &rarr;
113 &rarr;
114 ## Target
114 ## Target
115 %if c.pull_request.target_ref_parts.type == 'branch':
115 %if c.pull_request.target_ref_parts.type == 'branch':
116 <a href="${h.route_path('repo_commits', repo_name=c.pull_request.target_repo.repo_name, _query=dict(branch=c.pull_request.target_ref_parts.name))}"><code class="pr-target-info">${c.pull_request.target_ref_parts.type}:${c.pull_request.target_ref_parts.name}</code></a>
116 <a href="${h.route_path('repo_commits', repo_name=c.pull_request.target_repo.repo_name, _query=dict(branch=c.pull_request.target_ref_parts.name))}"><code class="pr-target-info">${c.pull_request.target_ref_parts.type}:${c.pull_request.target_ref_parts.name}</code></a>
117 %else:
117 %else:
118 <code class="pr-target-info">${'{}:{}'.format(c.pull_request.target_ref_parts.type, c.pull_request.target_ref_parts.name)}</code>
118 <code class="pr-target-info">${'{}:{}'.format(c.pull_request.target_ref_parts.type, c.pull_request.target_ref_parts.name)}</code>
119 %endif
119 %endif
120
120
121 ${_('of')} <a href="${h.route_path('repo_summary', repo_name=c.pull_request.target_repo.repo_name)}">${c.pull_request.target_repo.repo_name}</a>
121 ${_('of')} <a href="${h.route_path('repo_summary', repo_name=c.pull_request.target_repo.repo_name)}">${c.pull_request.target_repo.repo_name}</a>
122
122
123 <a class="source-details-action" href="#expand-source-details" onclick="return toggleElement(this, '.source-details')" data-toggle-on='<i class="icon-angle-down">more details</i>' data-toggle-off='<i class="icon-angle-up">less details</i>'>
123 <a class="source-details-action" href="#expand-source-details" onclick="return toggleElement(this, '.source-details')" data-toggle-on='<i class="icon-angle-down">more details</i>' data-toggle-off='<i class="icon-angle-up">less details</i>'>
124 <i class="icon-angle-down">more details</i>
124 <i class="icon-angle-down">more details</i>
125 </a>
125 </a>
126
126
127 </div>
127 </div>
128
128
129 <div class="source-details" style="display: none">
129 <div class="source-details" style="display: none">
130
130
131 <ul>
131 <ul>
132
132
133 ## common ancestor
133 ## common ancestor
134 <li>
134 <li>
135 ${_('Common ancestor')}:
135 ${_('Common ancestor')}:
136 % if c.ancestor_commit:
136 % if c.ancestor_commit:
137 <a href="${h.route_path('repo_commit', repo_name=c.target_repo.repo_name, commit_id=c.ancestor_commit.raw_id)}">${h.show_id(c.ancestor_commit)}</a>
137 <a href="${h.route_path('repo_commit', repo_name=c.target_repo.repo_name, commit_id=c.ancestor_commit.raw_id)}">${h.show_id(c.ancestor_commit)}</a>
138 % else:
138 % else:
139 ${_('not available')}
139 ${_('not available')}
140 % endif
140 % endif
141 </li>
141 </li>
142
142
143 ## pull url
143 ## pull url
144 <li>
144 <li>
145 %if h.is_hg(c.pull_request.source_repo):
145 %if h.is_hg(c.pull_request.source_repo):
146 <% clone_url = 'hg pull -r {} {}'.format(h.short_id(c.source_ref), c.pull_request.source_repo.clone_url()) %>
146 <% clone_url = 'hg pull -r {} {}'.format(h.short_id(c.source_ref), c.pull_request.source_repo.clone_url()) %>
147 %elif h.is_git(c.pull_request.source_repo):
147 %elif h.is_git(c.pull_request.source_repo):
148 <% clone_url = 'git pull {} {}'.format(c.pull_request.source_repo.clone_url(), c.pull_request.source_ref_parts.name) %>
148 <% clone_url = 'git pull {} {}'.format(c.pull_request.source_repo.clone_url(), c.pull_request.source_ref_parts.name) %>
149 %endif
149 %endif
150
150
151 <span>${_('Pull changes from source')}</span>: <input type="text" class="input-monospace pr-pullinfo" value="${clone_url}" readonly="readonly">
151 <span>${_('Pull changes from source')}</span>: <input type="text" class="input-monospace pr-pullinfo" value="${clone_url}" readonly="readonly">
152 <i class="tooltip icon-clipboard clipboard-action pull-right pr-pullinfo-copy" data-clipboard-text="${clone_url}" title="${_('Copy the pull url')}"></i>
152 <i class="tooltip icon-clipboard clipboard-action pull-right pr-pullinfo-copy" data-clipboard-text="${clone_url}" title="${_('Copy the pull url')}"></i>
153 </li>
153 </li>
154
154
155 ## Shadow repo
155 ## Shadow repo
156 <li>
156 <li>
157 % if not c.pull_request.is_closed() and c.pull_request.shadow_merge_ref:
157 % if not c.pull_request.is_closed() and c.pull_request.shadow_merge_ref:
158 %if h.is_hg(c.pull_request.target_repo):
158 %if h.is_hg(c.pull_request.target_repo):
159 <% clone_url = 'hg clone --update {} {} pull-request-{}'.format(c.pull_request.shadow_merge_ref.name, c.shadow_clone_url, c.pull_request.pull_request_id) %>
159 <% clone_url = 'hg clone --update {} {} pull-request-{}'.format(c.pull_request.shadow_merge_ref.name, c.shadow_clone_url, c.pull_request.pull_request_id) %>
160 %elif h.is_git(c.pull_request.target_repo):
160 %elif h.is_git(c.pull_request.target_repo):
161 <% clone_url = 'git clone --branch {} {} pull-request-{}'.format(c.pull_request.shadow_merge_ref.name, c.shadow_clone_url, c.pull_request.pull_request_id) %>
161 <% clone_url = 'git clone --branch {} {} pull-request-{}'.format(c.pull_request.shadow_merge_ref.name, c.shadow_clone_url, c.pull_request.pull_request_id) %>
162 %endif
162 %endif
163
163
164 <span class="tooltip" title="${_('Clone repository in its merged state using shadow repository')}">${_('Clone from shadow repository')}</span>: <input type="text" class="input-monospace pr-mergeinfo" value="${clone_url}" readonly="readonly">
164 <span class="tooltip" title="${_('Clone repository in its merged state using shadow repository')}">${_('Clone from shadow repository')}</span>: <input type="text" class="input-monospace pr-mergeinfo" value="${clone_url}" readonly="readonly">
165 <i class="tooltip icon-clipboard clipboard-action pull-right pr-mergeinfo-copy" data-clipboard-text="${clone_url}" title="${_('Copy the clone url')}"></i>
165 <i class="tooltip icon-clipboard clipboard-action pull-right pr-mergeinfo-copy" data-clipboard-text="${clone_url}" title="${_('Copy the clone url')}"></i>
166
166
167 % else:
167 % else:
168 <div class="">
168 <div class="">
169 ${_('Shadow repository data not available')}.
169 ${_('Shadow repository data not available')}.
170 </div>
170 </div>
171 % endif
171 % endif
172 </li>
172 </li>
173
173
174 </ul>
174 </ul>
175
175
176 </div>
176 </div>
177
177
178 </div>
178 </div>
179
179
180 </div>
180 </div>
181
181
182 ## versions
182 ## versions
183 <div class="field">
183 <div class="field">
184 <div class="label-pr-detail">
184 <div class="label-pr-detail">
185 <label>${_('Versions')}:</label>
185 <label>${_('Versions')}:</label>
186 </div>
186 </div>
187
187
188 <% outdated_comm_count_ver = len(c.inline_versions[None]['outdated']) %>
188 <% outdated_comm_count_ver = len(c.inline_versions[None]['outdated']) %>
189 <% general_outdated_comm_count_ver = len(c.comment_versions[None]['outdated']) %>
189 <% general_outdated_comm_count_ver = len(c.comment_versions[None]['outdated']) %>
190
190
191 <div class="pr-versions">
191 <div class="pr-versions">
192 % if c.show_version_changes:
192 % if c.show_version_changes:
193 <% outdated_comm_count_ver = len(c.inline_versions[c.at_version_num]['outdated']) %>
193 <% outdated_comm_count_ver = len(c.inline_versions[c.at_version_num]['outdated']) %>
194 <% general_outdated_comm_count_ver = len(c.comment_versions[c.at_version_num]['outdated']) %>
194 <% general_outdated_comm_count_ver = len(c.comment_versions[c.at_version_num]['outdated']) %>
195 ${_ungettext('{} version available for this pull request, ', '{} versions available for this pull request, ', len(c.versions)).format(len(c.versions))}
195 ${_ungettext('{} version available for this pull request, ', '{} versions available for this pull request, ', len(c.versions)).format(len(c.versions))}
196 <a id="show-pr-versions" onclick="return versionController.toggleVersionView(this)" href="#show-pr-versions"
196 <a id="show-pr-versions" onclick="return versionController.toggleVersionView(this)" href="#show-pr-versions"
197 data-toggle-on="${_('show versions')}."
197 data-toggle-on="${_('show versions')}."
198 data-toggle-off="${_('hide versions')}.">
198 data-toggle-off="${_('hide versions')}.">
199 ${_('show versions')}.
199 ${_('show versions')}.
200 </a>
200 </a>
201 <table>
201 <table>
202 ## SHOW ALL VERSIONS OF PR
202 ## SHOW ALL VERSIONS OF PR
203 <% ver_pr = None %>
203 <% ver_pr = None %>
204
204
205 % for data in reversed(list(enumerate(c.versions, 1))):
205 % for data in reversed(list(enumerate(c.versions, 1))):
206 <% ver_pos = data[0] %>
206 <% ver_pos = data[0] %>
207 <% ver = data[1] %>
207 <% ver = data[1] %>
208 <% ver_pr = ver.pull_request_version_id %>
208 <% ver_pr = ver.pull_request_version_id %>
209 <% display_row = '' if c.at_version and (c.at_version_num == ver_pr or c.from_version_num == ver_pr) else 'none' %>
209 <% display_row = '' if c.at_version and (c.at_version_num == ver_pr or c.from_version_num == ver_pr) else 'none' %>
210
210
211 <tr class="version-pr" style="display: ${display_row}">
211 <tr class="version-pr" style="display: ${display_row}">
212 <td>
212 <td>
213 <code>
213 <code>
214 <a href="${request.current_route_path(_query=dict(version=ver_pr or 'latest'))}">v${ver_pos}</a>
214 <a href="${request.current_route_path(_query=dict(version=ver_pr or 'latest'))}">v${ver_pos}</a>
215 </code>
215 </code>
216 </td>
216 </td>
217 <td>
217 <td>
218 <input ${('checked="checked"' if c.from_version_index == ver_pr else '')} class="compare-radio-button" type="radio" name="ver_source" value="${ver_pr or 'latest'}" data-ver-pos="${ver_pos}"/>
218 <input ${('checked="checked"' if c.from_version_index == ver_pr else '')} class="compare-radio-button" type="radio" name="ver_source" value="${ver_pr or 'latest'}" data-ver-pos="${ver_pos}"/>
219 <input ${('checked="checked"' if c.at_version_num == ver_pr else '')} class="compare-radio-button" type="radio" name="ver_target" value="${ver_pr or 'latest'}" data-ver-pos="${ver_pos}"/>
219 <input ${('checked="checked"' if c.at_version_num == ver_pr else '')} class="compare-radio-button" type="radio" name="ver_target" value="${ver_pr or 'latest'}" data-ver-pos="${ver_pos}"/>
220 </td>
220 </td>
221 <td>
221 <td>
222 <% review_status = c.review_versions[ver_pr].status if ver_pr in c.review_versions else 'not_reviewed' %>
222 <% review_status = c.review_versions[ver_pr].status if ver_pr in c.review_versions else 'not_reviewed' %>
223 <i class="tooltip icon-circle review-status-${review_status}" title="${_('Your review status at this version')}"></i>
223 <i class="tooltip icon-circle review-status-${review_status}" title="${_('Your review status at this version')}"></i>
224
224
225 </td>
225 </td>
226 <td>
226 <td>
227 % if c.at_version_num != ver_pr:
227 % if c.at_version_num != ver_pr:
228 <i class="tooltip icon-comment" title="${_('Comments from pull request version v{0}').format(ver_pos)}"></i>
228 <i class="tooltip icon-comment" title="${_('Comments from pull request version v{0}').format(ver_pos)}"></i>
229 <code>
229 <code>
230 General:${len(c.comment_versions[ver_pr]['at'])} / Inline:${len(c.inline_versions[ver_pr]['at'])}
230 General:${len(c.comment_versions[ver_pr]['at'])} / Inline:${len(c.inline_versions[ver_pr]['at'])}
231 </code>
231 </code>
232 % endif
232 % endif
233 </td>
233 </td>
234 <td>
234 <td>
235 ##<code>${ver.source_ref_parts.commit_id[:6]}</code>
235 ##<code>${ver.source_ref_parts.commit_id[:6]}</code>
236 </td>
236 </td>
237 <td>
237 <td>
238 <code>${h.age_component(ver.updated_on, time_is_local=True, tooltip=False)}</code>
238 <code>${h.age_component(ver.updated_on, time_is_local=True, tooltip=False)}</code>
239 </td>
239 </td>
240 </tr>
240 </tr>
241 % endfor
241 % endfor
242
242
243 <tr>
243 <tr>
244 <td colspan="6">
244 <td colspan="6">
245 <button id="show-version-diff" onclick="return versionController.showVersionDiff()" class="btn btn-sm" style="display: none"
245 <button id="show-version-diff" onclick="return versionController.showVersionDiff()" class="btn btn-sm" style="display: none"
246 data-label-text-locked="${_('select versions to show changes')}"
246 data-label-text-locked="${_('select versions to show changes')}"
247 data-label-text-diff="${_('show changes between versions')}"
247 data-label-text-diff="${_('show changes between versions')}"
248 data-label-text-show="${_('show pull request for this version')}"
248 data-label-text-show="${_('show pull request for this version')}"
249 >
249 >
250 ${_('select versions to show changes')}
250 ${_('select versions to show changes')}
251 </button>
251 </button>
252 </td>
252 </td>
253 </tr>
253 </tr>
254 </table>
254 </table>
255 % else:
255 % else:
256 <div>
256 <div>
257 ${_('Pull request versions not available')}.
257 ${_('Pull request versions not available')}.
258 </div>
258 </div>
259 % endif
259 % endif
260 </div>
260 </div>
261 </div>
261 </div>
262
262
263 </div>
263 </div>
264
264
265 </div>
265 </div>
266
266
267
267
268 </div>
268 </div>
269
269
270 </div>
270 </div>
271
271
272 <div class="box">
272 <div class="box">
273
273
274 % if c.state_progressing:
274 % if c.state_progressing:
275
275
276 <h2 style="text-align: center">
276 <h2 style="text-align: center">
277 ${_('Cannot show diff when pull request state is changing. Current progress state')}: <span class="tag tag-merge-state-${c.pull_request.state}">${c.pull_request.state}</span>
277 ${_('Cannot show diff when pull request state is changing. Current progress state')}: <span class="tag tag-merge-state-${c.pull_request.state}">${c.pull_request.state}</span>
278
278
279 % if c.is_super_admin:
279 % if c.is_super_admin:
280 <br/>
280 <br/>
281 If you think this is an error try <a href="${h.current_route_path(request, force_state='created')}">forced state reset</a> to <span class="tag tag-merge-state-created">created</span> state.
281 If you think this is an error try <a href="${h.current_route_path(request, force_state='created')}">forced state reset</a> to <span class="tag tag-merge-state-created">created</span> state.
282 % endif
282 % endif
283 </h2>
283 </h2>
284
284
285 % else:
285 % else:
286
286
287 ## Diffs rendered here
287 ## Diffs rendered here
288 <div class="table" >
288 <div class="table" >
289 <div id="changeset_compare_view_content">
289 <div id="changeset_compare_view_content">
290 ##CS
290 ##CS
291 % if c.missing_requirements:
291 % if c.missing_requirements:
292 <div class="box">
292 <div class="box">
293 <div class="alert alert-warning">
293 <div class="alert alert-warning">
294 <div>
294 <div>
295 <strong>${_('Missing requirements:')}</strong>
295 <strong>${_('Missing requirements:')}</strong>
296 ${_('These commits cannot be displayed, because this repository uses the Mercurial largefiles extension, which was not enabled.')}
296 ${_('These commits cannot be displayed, because this repository uses the Mercurial largefiles extension, which was not enabled.')}
297 </div>
297 </div>
298 </div>
298 </div>
299 </div>
299 </div>
300 % elif c.missing_commits:
300 % elif c.missing_commits:
301 <div class="box">
301 <div class="box">
302 <div class="alert alert-warning">
302 <div class="alert alert-warning">
303 <div>
303 <div>
304 <strong>${_('Missing commits')}:</strong>
304 <strong>${_('Missing commits')}:</strong>
305 ${_('This pull request cannot be displayed, because one or more commits no longer exist in the source repository.')}<br/>
305 ${_('This pull request cannot be displayed, because one or more commits no longer exist in the source repository.')}<br/>
306 ${_('Please update this pull request, push the commits back into the source repository, or consider closing this pull request.')}<br/>
306 ${_('Please update this pull request, push the commits back into the source repository, or consider closing this pull request.')}<br/>
307 ${_('Consider doing a `force update commits` in case you think this is an error.')}
307 ${_('Consider doing a `force update commits` in case you think this is an error.')}
308 </div>
308 </div>
309 </div>
309 </div>
310 </div>
310 </div>
311 % elif c.pr_merge_source_commit.changed and not c.pull_request.is_closed():
311 % elif c.pr_merge_source_commit.changed and not c.pull_request.is_closed():
312 <div class="box">
312 <div class="box">
313 <div class="alert alert-info">
313 <div class="alert alert-info">
314 <div>
314 <div>
315 <strong>${_('There are new changes for `{}:{}` in source repository, please consider updating this pull request.').format(c.pr_merge_source_commit.ref_spec.type, c.pr_merge_source_commit.ref_spec.name)}</strong>
315 <strong>${_('There are new changes for `{}:{}` in source repository, please consider updating this pull request.').format(c.pr_merge_source_commit.ref_spec.type, c.pr_merge_source_commit.ref_spec.name)}</strong>
316 </div>
316 </div>
317 </div>
317 </div>
318 </div>
318 </div>
319 % endif
319 % endif
320
320
321 <div class="compare_view_commits_title">
321 <div class="compare_view_commits_title">
322 % if not c.compare_mode:
322 % if not c.compare_mode:
323
323
324 % if c.at_version_index:
324 % if c.at_version_index:
325 <h4>
325 <h4>
326 ${_('Showing changes at v{}, commenting is disabled.').format(c.at_version_index)}
326 ${_('Showing changes at v{}, commenting is disabled.').format(c.at_version_index)}
327 </h4>
327 </h4>
328 % endif
328 % endif
329
329
330 <div class="pull-left">
330 <div class="pull-left">
331 <div class="btn-group">
331 <div class="btn-group">
332 <a class="${('collapsed' if c.collapse_all_commits else '')}" href="#expand-commits" onclick="toggleCommitExpand(this); return false" data-toggle-commits-cnt=${len(c.commit_ranges)} >
332 <a class="${('collapsed' if c.collapse_all_commits else '')}" href="#expand-commits" onclick="toggleCommitExpand(this); return false" data-toggle-commits-cnt=${len(c.commit_ranges)} >
333 % if c.collapse_all_commits:
333 % if c.collapse_all_commits:
334 <i class="icon-plus-squared-alt icon-no-margin"></i>
334 <i class="icon-plus-squared-alt icon-no-margin"></i>
335 ${_ungettext('Expand {} commit', 'Expand {} commits', len(c.commit_ranges)).format(len(c.commit_ranges))}
335 ${_ungettext('Expand {} commit', 'Expand {} commits', len(c.commit_ranges)).format(len(c.commit_ranges))}
336 % else:
336 % else:
337 <i class="icon-minus-squared-alt icon-no-margin"></i>
337 <i class="icon-minus-squared-alt icon-no-margin"></i>
338 ${_ungettext('Collapse {} commit', 'Collapse {} commits', len(c.commit_ranges)).format(len(c.commit_ranges))}
338 ${_ungettext('Collapse {} commit', 'Collapse {} commits', len(c.commit_ranges)).format(len(c.commit_ranges))}
339 % endif
339 % endif
340 </a>
340 </a>
341 </div>
341 </div>
342 </div>
342 </div>
343
343
344 <div class="pull-right">
344 <div class="pull-right">
345 % if c.allowed_to_update and not c.pull_request.is_closed():
345 % if c.allowed_to_update and not c.pull_request.is_closed():
346
346
347 <div class="btn-group btn-group-actions">
347 <div class="btn-group btn-group-actions">
348 <a id="update_commits" class="btn btn-primary no-margin" onclick="updateController.updateCommits(this); return false">
348 <a id="update_commits" class="btn btn-primary no-margin" onclick="updateController.updateCommits(this); return false">
349 ${_('Update commits')}
349 ${_('Update commits')}
350 </a>
350 </a>
351
351
352 <a id="update_commits_switcher" class="tooltip btn btn-primary btn-more-option" data-toggle="dropdown" aria-pressed="false" role="button" title="${_('more update options')}">
352 <a id="update_commits_switcher" class="tooltip btn btn-primary btn-more-option" data-toggle="dropdown" aria-pressed="false" role="button" title="${_('more update options')}">
353 <i class="icon-down"></i>
353 <i class="icon-down"></i>
354 </a>
354 </a>
355
355
356 <div class="btn-action-switcher-container right-align" id="update-commits-switcher">
356 <div class="btn-action-switcher-container right-align" id="update-commits-switcher">
357 <ul class="btn-action-switcher" role="menu" style="min-width: 300px;">
357 <ul class="btn-action-switcher" role="menu" style="min-width: 300px;">
358 <li>
358 <li>
359 <a href="#forceUpdate" onclick="updateController.forceUpdateCommits(this); return false">
359 <a href="#forceUpdate" onclick="updateController.forceUpdateCommits(this); return false">
360 ${_('Force update commits')}
360 ${_('Force update commits')}
361 </a>
361 </a>
362 <div class="action-help-block">
362 <div class="action-help-block">
363 ${_('Update commits and force refresh this pull request.')}
363 ${_('Update commits and force refresh this pull request.')}
364 </div>
364 </div>
365 </li>
365 </li>
366 </ul>
366 </ul>
367 </div>
367 </div>
368 </div>
368 </div>
369
369
370 % else:
370 % else:
371 <a class="tooltip btn disabled pull-right" disabled="disabled" title="${_('Update is disabled for current view')}">${_('Update commits')}</a>
371 <a class="tooltip btn disabled pull-right" disabled="disabled" title="${_('Update is disabled for current view')}">${_('Update commits')}</a>
372 % endif
372 % endif
373
373
374 </div>
374 </div>
375 % endif
375 % endif
376 </div>
376 </div>
377
377
378 % if not c.missing_commits:
378 % if not c.missing_commits:
379 ## COMPARE RANGE DIFF MODE
379 ## COMPARE RANGE DIFF MODE
380 % if c.compare_mode:
380 % if c.compare_mode:
381 % if c.at_version:
381 % if c.at_version:
382 <h4>
382 <h4>
383 ${_('Commits and changes between v{ver_from} and {ver_to} of this pull request, commenting is disabled').format(ver_from=c.from_version_index, ver_to=c.at_version_index if c.at_version_index else 'latest')}:
383 ${_('Commits and changes between v{ver_from} and {ver_to} of this pull request, commenting is disabled').format(ver_from=c.from_version_index, ver_to=c.at_version_index if c.at_version_index else 'latest')}:
384 </h4>
384 </h4>
385
385
386 <div class="subtitle-compare">
386 <div class="subtitle-compare">
387 ${_('commits added: {}, removed: {}').format(len(c.commit_changes_summary.added), len(c.commit_changes_summary.removed))}
387 ${_('commits added: {}, removed: {}').format(len(c.commit_changes_summary.added), len(c.commit_changes_summary.removed))}
388 </div>
388 </div>
389
389
390 <div class="container">
390 <div class="container">
391 <table class="rctable compare_view_commits">
391 <table class="rctable compare_view_commits">
392 <tr>
392 <tr>
393 <th></th>
393 <th></th>
394 <th>${_('Time')}</th>
394 <th>${_('Time')}</th>
395 <th>${_('Author')}</th>
395 <th>${_('Author')}</th>
396 <th>${_('Commit')}</th>
396 <th>${_('Commit')}</th>
397 <th></th>
397 <th></th>
398 <th>${_('Description')}</th>
398 <th>${_('Description')}</th>
399 </tr>
399 </tr>
400
400
401 % for c_type, commit in c.commit_changes:
401 % for c_type, commit in c.commit_changes:
402 % if c_type in ['a', 'r']:
402 % if c_type in ['a', 'r']:
403 <%
403 <%
404 if c_type == 'a':
404 if c_type == 'a':
405 cc_title = _('Commit added in displayed changes')
405 cc_title = _('Commit added in displayed changes')
406 elif c_type == 'r':
406 elif c_type == 'r':
407 cc_title = _('Commit removed in displayed changes')
407 cc_title = _('Commit removed in displayed changes')
408 else:
408 else:
409 cc_title = ''
409 cc_title = ''
410 %>
410 %>
411 <tr id="row-${commit.raw_id}" commit_id="${commit.raw_id}" class="compare_select">
411 <tr id="row-${commit.raw_id}" commit_id="${commit.raw_id}" class="compare_select">
412 <td>
412 <td>
413 <div class="commit-change-indicator color-${c_type}-border">
413 <div class="commit-change-indicator color-${c_type}-border">
414 <div class="commit-change-content color-${c_type} tooltip" title="${h.tooltip(cc_title)}">
414 <div class="commit-change-content color-${c_type} tooltip" title="${h.tooltip(cc_title)}">
415 ${c_type.upper()}
415 ${c_type.upper()}
416 </div>
416 </div>
417 </div>
417 </div>
418 </td>
418 </td>
419 <td class="td-time">
419 <td class="td-time">
420 ${h.age_component(commit.date)}
420 ${h.age_component(commit.date)}
421 </td>
421 </td>
422 <td class="td-user">
422 <td class="td-user">
423 ${base.gravatar_with_user(commit.author, 16, tooltip=True)}
423 ${base.gravatar_with_user(commit.author, 16, tooltip=True)}
424 </td>
424 </td>
425 <td class="td-hash">
425 <td class="td-hash">
426 <code>
426 <code>
427 <a href="${h.route_path('repo_commit', repo_name=c.target_repo.repo_name, commit_id=commit.raw_id)}">
427 <a href="${h.route_path('repo_commit', repo_name=c.target_repo.repo_name, commit_id=commit.raw_id)}">
428 r${commit.idx}:${h.short_id(commit.raw_id)}
428 r${commit.idx}:${h.short_id(commit.raw_id)}
429 </a>
429 </a>
430 ${h.hidden('revisions', commit.raw_id)}
430 ${h.hidden('revisions', commit.raw_id)}
431 </code>
431 </code>
432 </td>
432 </td>
433 <td class="td-message expand_commit" data-commit-id="${commit.raw_id}" title="${_( 'Expand commit message')}" onclick="commitsController.expandCommit(this); return false">
433 <td class="td-message expand_commit" data-commit-id="${commit.raw_id}" title="${_( 'Expand commit message')}" onclick="commitsController.expandCommit(this); return false">
434 <i class="icon-expand-linked"></i>
434 <i class="icon-expand-linked"></i>
435 </td>
435 </td>
436 <td class="mid td-description">
436 <td class="mid td-description">
437 <div class="log-container truncate-wrap">
437 <div class="log-container truncate-wrap">
438 <div class="message truncate" id="c-${commit.raw_id}" data-message-raw="${commit.message}">${h.urlify_commit_message(commit.message, c.repo_name, issues_container=c.referenced_commit_issues)}</div>
438 <div class="message truncate" id="c-${commit.raw_id}" data-message-raw="${commit.message}">${h.urlify_commit_message(commit.message, c.repo_name, issues_container=c.referenced_commit_issues)}</div>
439 </div>
439 </div>
440 </td>
440 </td>
441 </tr>
441 </tr>
442 % endif
442 % endif
443 % endfor
443 % endfor
444 </table>
444 </table>
445 </div>
445 </div>
446
446
447 % endif
447 % endif
448
448
449 ## Regular DIFF
449 ## Regular DIFF
450 % else:
450 % else:
451 <%include file="/compare/compare_commits.mako" />
451 <%include file="/compare/compare_commits.mako" />
452 % endif
452 % endif
453
453
454 <div class="cs_files">
454 <div class="cs_files">
455 <%namespace name="cbdiffs" file="/codeblocks/diffs.mako"/>
455 <%namespace name="cbdiffs" file="/codeblocks/diffs.mako"/>
456
456
457 <%
457 <%
458 pr_menu_data = {
458 pr_menu_data = {
459 'outdated_comm_count_ver': outdated_comm_count_ver,
459 'outdated_comm_count_ver': outdated_comm_count_ver,
460 'pull_request': c.pull_request
460 'pull_request': c.pull_request
461 }
461 }
462 %>
462 %>
463
463
464 ${cbdiffs.render_diffset_menu(c.diffset, range_diff_on=c.range_diff_on, pull_request_menu=pr_menu_data)}
464 ${cbdiffs.render_diffset_menu(c.diffset, range_diff_on=c.range_diff_on, pull_request_menu=pr_menu_data)}
465
465
466 % if c.range_diff_on:
466 % if c.range_diff_on:
467 % for commit in c.commit_ranges:
467 % for commit in c.commit_ranges:
468 ${cbdiffs.render_diffset(
468 ${cbdiffs.render_diffset(
469 c.changes[commit.raw_id],
469 c.changes[commit.raw_id],
470 commit=commit, use_comments=True,
470 commit=commit, use_comments=True,
471 collapse_when_files_over=5,
471 collapse_when_files_over=5,
472 disable_new_comments=True,
472 disable_new_comments=True,
473 deleted_files_comments=c.deleted_files_comments,
473 deleted_files_comments=c.deleted_files_comments,
474 inline_comments=c.inline_comments,
474 inline_comments=c.inline_comments,
475 pull_request_menu=pr_menu_data, show_todos=False)}
475 pull_request_menu=pr_menu_data, show_todos=False)}
476 % endfor
476 % endfor
477 % else:
477 % else:
478 ${cbdiffs.render_diffset(
478 ${cbdiffs.render_diffset(
479 c.diffset, use_comments=True,
479 c.diffset, use_comments=True,
480 collapse_when_files_over=30,
480 collapse_when_files_over=30,
481 disable_new_comments=not c.allowed_to_comment,
481 disable_new_comments=not c.allowed_to_comment,
482 deleted_files_comments=c.deleted_files_comments,
482 deleted_files_comments=c.deleted_files_comments,
483 inline_comments=c.inline_comments,
483 inline_comments=c.inline_comments,
484 pull_request_menu=pr_menu_data, show_todos=False)}
484 pull_request_menu=pr_menu_data, show_todos=False)}
485 % endif
485 % endif
486
486
487 </div>
487 </div>
488 % else:
488 % else:
489 ## skipping commits we need to clear the view for missing commits
489 ## skipping commits we need to clear the view for missing commits
490 <div style="clear:both;"></div>
490 <div style="clear:both;"></div>
491 % endif
491 % endif
492
492
493 </div>
493 </div>
494 </div>
494 </div>
495
495
496 ## template for inline comment form
496 ## template for inline comment form
497 <%namespace name="comment" file="/changeset/changeset_file_comment.mako"/>
497 <%namespace name="comment" file="/changeset/changeset_file_comment.mako"/>
498
498
499 ## comments heading with count
499 ## comments heading with count
500 <div class="comments-heading">
500 <div class="comments-heading">
501 <i class="icon-comment"></i>
501 <i class="icon-comment"></i>
502 ${_('General Comments')} ${len(c.comments)}
502 ${_('General Comments')} ${len(c.comments)}
503 </div>
503 </div>
504
504
505 ## render general comments
505 ## render general comments
506 <div id="comment-tr-show">
506 <div id="comment-tr-show">
507 % if general_outdated_comm_count_ver:
507 % if general_outdated_comm_count_ver:
508 <div class="info-box">
508 <div class="info-box">
509 % if general_outdated_comm_count_ver == 1:
509 % if general_outdated_comm_count_ver == 1:
510 ${_('there is {num} general comment from older versions').format(num=general_outdated_comm_count_ver)},
510 ${_('there is {num} general comment from older versions').format(num=general_outdated_comm_count_ver)},
511 <a href="#show-hidden-comments" onclick="$('.comment-general.comment-outdated').show(); $(this).parent().hide(); return false;">${_('show it')}</a>
511 <a href="#show-hidden-comments" onclick="$('.comment-general.comment-outdated').show(); $(this).parent().hide(); return false;">${_('show it')}</a>
512 % else:
512 % else:
513 ${_('there are {num} general comments from older versions').format(num=general_outdated_comm_count_ver)},
513 ${_('there are {num} general comments from older versions').format(num=general_outdated_comm_count_ver)},
514 <a href="#show-hidden-comments" onclick="$('.comment-general.comment-outdated').show(); $(this).parent().hide(); return false;">${_('show them')}</a>
514 <a href="#show-hidden-comments" onclick="$('.comment-general.comment-outdated').show(); $(this).parent().hide(); return false;">${_('show them')}</a>
515 % endif
515 % endif
516 </div>
516 </div>
517 % endif
517 % endif
518 </div>
518 </div>
519
519
520 ${comment.generate_comments(c.comments, include_pull_request=True, is_pull_request=True)}
520 ${comment.generate_comments(c.comments, include_pull_request=True, is_pull_request=True)}
521
521
522 % if not c.pull_request.is_closed():
522 % if not c.pull_request.is_closed():
523 ## main comment form and it status
523 ## main comment form and it status
524 ${comment.comments(h.route_path('pullrequest_comment_create', repo_name=c.repo_name,
524 ${comment.comments(h.route_path('pullrequest_comment_create', repo_name=c.repo_name,
525 pull_request_id=c.pull_request.pull_request_id),
525 pull_request_id=c.pull_request.pull_request_id),
526 c.pull_request_review_status,
526 c.pull_request_review_status,
527 is_pull_request=True, change_status=c.allowed_to_change_status)}
527 is_pull_request=True, change_status=c.allowed_to_change_status)}
528
528
529 ## merge status, and merge action
529 ## merge status, and merge action
530 <div class="pull-request-merge">
530 <div class="pull-request-merge">
531 <%include file="/pullrequests/pullrequest_merge_checks.mako"/>
531 <%include file="/pullrequests/pullrequest_merge_checks.mako"/>
532 </div>
532 </div>
533
533
534 %endif
534 %endif
535
535
536 % endif
536 % endif
537 </div>
537 </div>
538
538
539
539
540 ### NAV SIDEBAR
540 ### NAV SIDEBAR
541 <aside class="right-sidebar right-sidebar-expanded" id="pr-nav-sticky" style="display: none">
541 <aside class="right-sidebar right-sidebar-expanded" id="pr-nav-sticky" style="display: none">
542 <div class="sidenav navbar__inner" >
542 <div class="sidenav navbar__inner" >
543 ## TOGGLE
543 ## TOGGLE
544 <div class="sidebar-toggle" onclick="toggleSidebar(); return false">
544 <div class="sidebar-toggle" onclick="toggleSidebar(); return false">
545 <a href="#toggleSidebar" class="grey-link-action">
545 <a href="#toggleSidebar" class="grey-link-action">
546
546
547 </a>
547 </a>
548 </div>
548 </div>
549
549
550 ## CONTENT
550 ## CONTENT
551 <div class="sidebar-content">
551 <div class="sidebar-content">
552
552
553 ## Drafts
553 ## Drafts
554 % if c.rhodecode_edition_id == 'EE':
554 % if c.rhodecode_edition_id == 'EE':
555 <div id="draftsTable" class="sidebar-element clear-both" style="display: ${'block' if c.draft_comments else 'none'}">
555 <div id="draftsTable" class="sidebar-element clear-both" style="display: ${'block' if c.draft_comments else 'none'}">
556 <div class="tooltip right-sidebar-collapsed-state" style="display: none;" onclick="toggleSidebar(); return false" title="${_('Drafts')}">
556 <div class="tooltip right-sidebar-collapsed-state" style="display: none;" onclick="toggleSidebar(); return false" title="${_('Drafts')}">
557 <i class="icon-comment icon-draft"></i>
557 <i class="icon-comment icon-draft"></i>
558 <span id="drafts-count">${len(c.draft_comments)}</span>
558 <span id="drafts-count">${len(c.draft_comments)}</span>
559 </div>
559 </div>
560
560
561 <div class="right-sidebar-expanded-state pr-details-title">
561 <div class="right-sidebar-expanded-state pr-details-title">
562 <span style="padding-left: 2px">
562 <span style="padding-left: 2px">
563 <input name="select_all_drafts" type="checkbox" onclick="$('[name=submit_draft]').prop('checked', !$('[name=submit_draft]').prop('checked'))">
563 <input name="select_all_drafts" type="checkbox" onclick="$('[name=submit_draft]').prop('checked', !$('[name=submit_draft]').prop('checked'))">
564 </span>
564 </span>
565 <span class="sidebar-heading noselect" onclick="refreshDraftComments(); return false">
565 <span class="sidebar-heading noselect" onclick="refreshDraftComments(); return false">
566 <i class="icon-comment icon-draft"></i>
566 <i class="icon-comment icon-draft"></i>
567 ${_('Drafts')}
567 ${_('Drafts')}
568 </span>
568 </span>
569 <span class="block-right action_button last-item" onclick="submitDrafts(event)">${_('Submit')}</span>
569 <span class="block-right action_button last-item" onclick="submitDrafts(event)">${_('Submit')}</span>
570 </div>
570 </div>
571
571
572 <div id="drafts" class="right-sidebar-expanded-state pr-details-content reviewers">
572 <div id="drafts" class="right-sidebar-expanded-state pr-details-content reviewers">
573 % if c.draft_comments:
573 % if c.draft_comments:
574 ${sidebar.comments_table(c.draft_comments, len(c.draft_comments), draft_comments=True)}
574 ${sidebar.comments_table(c.draft_comments, len(c.draft_comments), draft_comments=True)}
575 % else:
575 % else:
576 <table class="drafts-content-table">
576 <table class="drafts-content-table">
577 <tr>
577 <tr>
578 <td>
578 <td>
579 ${_('No TODOs yet')}
579 ${_('No TODOs yet')}
580 </td>
580 </td>
581 </tr>
581 </tr>
582 </table>
582 </table>
583 % endif
583 % endif
584 </div>
584 </div>
585
585
586 </div>
586 </div>
587 % endif
587 % endif
588
588
589 ## RULES SUMMARY/RULES
589 ## RULES SUMMARY/RULES
590 <div class="sidebar-element clear-both">
590 <div class="sidebar-element clear-both">
591 <% vote_title = _ungettext(
591 <% vote_title = _ungettext(
592 'Status calculated based on votes from {} reviewer',
592 'Status calculated based on votes from {} reviewer',
593 'Status calculated based on votes from {} reviewers', c.reviewers_count).format(c.reviewers_count)
593 'Status calculated based on votes from {} reviewers', c.reviewers_count).format(c.reviewers_count)
594 %>
594 %>
595
595
596 <div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="${vote_title}">
596 <div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="${vote_title}">
597 <i class="icon-circle review-status-${c.pull_request_review_status}"></i>
597 <i class="icon-circle review-status-${c.pull_request_review_status}"></i>
598 ${c.reviewers_count}
598 ${c.reviewers_count}
599 </div>
599 </div>
600
600
601 ## REVIEWERS
601 ## REVIEWERS
602 <div class="right-sidebar-expanded-state pr-details-title">
602 <div class="right-sidebar-expanded-state pr-details-title">
603 <span class="tooltip sidebar-heading" title="${vote_title}">
603 <span class="tooltip sidebar-heading" title="${vote_title}">
604 <i class="icon-circle review-status-${c.pull_request_review_status}"></i>
604 <i class="icon-circle review-status-${c.pull_request_review_status}"></i>
605 ${_('Reviewers')}
605 ${_('Reviewers')}
606 </span>
606 </span>
607
607 %if c.allowed_to_update:
608 %if c.allowed_to_update:
608 <span id="open_edit_reviewers" class="block-right action_button last-item">${_('Edit')}</span>
609 <span id="open_edit_reviewers" class="block-right action_button last-item">${_('Edit')}</span>
609 <span id="close_edit_reviewers" class="block-right action_button last-item" style="display: none;">${_('Close')}</span>
610 <span id="close_edit_reviewers" class="block-right action_button last-item" style="display: none;">${_('Close')}</span>
610 %else:
611 %else:
611 <span id="open_edit_reviewers" class="block-right action_button last-item">${_('Show rules')}</span>
612 <span id="open_edit_reviewers" class="block-right action_button last-item">${_('Show rules')}</span>
612 <span id="close_edit_reviewers" class="block-right action_button last-item" style="display: none;">${_('Close')}</span>
613 <span id="close_edit_reviewers" class="block-right action_button last-item" style="display: none;">${_('Close')}</span>
613 %endif
614 %endif
614 </div>
615 </div>
615
616
616 <div id="reviewers" class="right-sidebar-expanded-state pr-details-content reviewers">
617 <div id="reviewers" class="right-sidebar-expanded-state pr-details-content reviewers">
617
618
618 <div id="review_rules" style="display: none" class="">
619 <div id="review_rules" style="display: none" class="">
619
620
620 <strong>${_('Reviewer rules')}</strong>
621 <strong>${_('Reviewer rules')}</strong>
621 <div class="pr-reviewer-rules">
622 <div class="pr-reviewer-rules">
622 ## review rules will be appended here, by default reviewers logic
623 ## review rules will be appended here, by default reviewers logic
623 </div>
624 </div>
624 <input id="review_data" type="hidden" name="review_data" value="">
625 <input id="review_data" type="hidden" name="review_data" value="">
625 </div>
626 </div>
626
627
627 ## members redering block
628 ## members redering block
628 <input type="hidden" name="__start__" value="review_members:sequence">
629 <input type="hidden" name="__start__" value="review_members:sequence">
629
630
630 <table id="review_members" class="group_members">
631 <table id="review_members" class="group_members">
631 ## This content is loaded via JS and ReviewersPanel
632 ## This content is loaded via JS and ReviewersPanel
632 </table>
633 </table>
633
634
634 <input type="hidden" name="__end__" value="review_members:sequence">
635 <input type="hidden" name="__end__" value="review_members:sequence">
635 ## end members redering block
636 ## end members redering block
636
637
637 %if not c.pull_request.is_closed():
638 %if not c.pull_request.is_closed():
638 <div id="add_reviewer" class="ac" style="display: none;">
639 <div id="add_reviewer" class="ac" style="display: none;">
639 %if c.allowed_to_update:
640 %if c.allowed_to_update:
640 % if not c.forbid_adding_reviewers:
641 % if not c.forbid_adding_reviewers:
641 <div id="add_reviewer_input" class="reviewer_ac" style="width: 240px">
642 <div id="add_reviewer_input" class="reviewer_ac" style="width: 240px">
642 <input class="ac-input" id="user" name="user" placeholder="${_('Add reviewer or reviewer group')}" type="text" autocomplete="off">
643 <input class="ac-input" id="user" name="user" placeholder="${_('Add reviewer or reviewer group')}" type="text" autocomplete="off">
643 <div id="reviewers_container"></div>
644 <div id="reviewers_container"></div>
644 </div>
645 </div>
645 % endif
646 % endif
646 <div class="pull-right" style="margin-bottom: 15px">
647 <div class="pull-right" style="margin-bottom: 15px">
647 <button data-role="reviewer" id="update_reviewers" class="btn btn-small no-margin">${_('Save Changes')}</button>
648 <button data-role="reviewer" id="update_reviewers" class="btn btn-sm no-margin">${_('Save Changes')}</button>
648 </div>
649 </div>
649 %endif
650 %endif
650 </div>
651 </div>
651 %endif
652 %endif
652 </div>
653 </div>
653 </div>
654 </div>
654
655
655 ## OBSERVERS
656 ## OBSERVERS
656 % if c.rhodecode_edition_id == 'EE':
657 % if c.rhodecode_edition_id == 'EE':
657 <div class="sidebar-element clear-both">
658 <div class="sidebar-element clear-both">
658 <% vote_title = _ungettext(
659 <% vote_title = _ungettext(
659 '{} observer without voting right.',
660 '{} observer without voting right.',
660 '{} observers without voting right.', c.observers_count).format(c.observers_count)
661 '{} observers without voting right.', c.observers_count).format(c.observers_count)
661 %>
662 %>
662
663
663 <div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="${vote_title}">
664 <div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="${vote_title}">
664 <i class="icon-circle-thin"></i>
665 <i class="icon-circle-thin"></i>
665 ${c.observers_count}
666 ${c.observers_count}
666 </div>
667 </div>
667
668
668 <div class="right-sidebar-expanded-state pr-details-title">
669 <div class="right-sidebar-expanded-state pr-details-title">
669 <span class="tooltip sidebar-heading" title="${vote_title}">
670 <span class="tooltip sidebar-heading" title="${vote_title}">
670 <i class="icon-circle-thin"></i>
671 <i class="icon-circle-thin"></i>
671 ${_('Observers')}
672 ${_('Observers')}
672 </span>
673 </span>
673 %if c.allowed_to_update:
674 %if c.allowed_to_update:
674 <span id="open_edit_observers" class="block-right action_button last-item">${_('Edit')}</span>
675 <span id="open_edit_observers" class="block-right action_button last-item">${_('Edit')}</span>
675 <span id="close_edit_observers" class="block-right action_button last-item" style="display: none;">${_('Close')}</span>
676 <span id="close_edit_observers" class="block-right action_button last-item" style="display: none;">${_('Close')}</span>
676 %endif
677 %endif
677 </div>
678 </div>
678
679
679 <div id="observers" class="right-sidebar-expanded-state pr-details-content reviewers">
680 <div id="observers" class="right-sidebar-expanded-state pr-details-content reviewers">
680 ## members redering block
681 ## members redering block
681 <input type="hidden" name="__start__" value="observer_members:sequence">
682 <input type="hidden" name="__start__" value="observer_members:sequence">
682
683
683 <table id="observer_members" class="group_members">
684 <table id="observer_members" class="group_members">
684 ## This content is loaded via JS and ReviewersPanel
685 ## This content is loaded via JS and ReviewersPanel
685 </table>
686 </table>
686
687
687 <input type="hidden" name="__end__" value="observer_members:sequence">
688 <input type="hidden" name="__end__" value="observer_members:sequence">
688 ## end members redering block
689 ## end members redering block
689
690
690 %if not c.pull_request.is_closed():
691 %if not c.pull_request.is_closed():
691 <div id="add_observer" class="ac" style="display: none;">
692 <div id="add_observer" class="ac" style="display: none;">
692 %if c.allowed_to_update:
693 %if c.allowed_to_update:
693 % if not c.forbid_adding_reviewers or 1:
694 % if not c.forbid_adding_reviewers or 1:
694 <div id="add_reviewer_input" class="reviewer_ac" style="width: 240px" >
695 <div id="add_reviewer_input" class="reviewer_ac" style="width: 240px" >
695 <input class="ac-input" id="observer" name="observer" placeholder="${_('Add observer or observer group')}" type="text" autocomplete="off">
696 <input class="ac-input" id="observer" name="observer" placeholder="${_('Add observer or observer group')}" type="text" autocomplete="off">
696 <div id="observers_container"></div>
697 <div id="observers_container"></div>
697 </div>
698 </div>
698 % endif
699 % endif
699 <div class="pull-right" style="margin-bottom: 15px">
700 <div class="pull-right" style="margin-bottom: 15px">
700 <button data-role="observer" id="update_observers" class="btn btn-small no-margin">${_('Save Changes')}</button>
701 <button data-role="observer" id="update_observers" class="btn btn-sm no-margin">${_('Save Changes')}</button>
701 </div>
702 </div>
702 %endif
703 %endif
703 </div>
704 </div>
704 %endif
705 %endif
705 </div>
706 </div>
706 </div>
707 </div>
707 % endif
708 % endif
708
709
709 ## TODOs
710 ## TODOs
710 <div id="todosTable" class="sidebar-element clear-both">
711 <div id="todosTable" class="sidebar-element clear-both">
711 <div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="TODOs">
712 <div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="TODOs">
712 <i class="icon-flag-filled"></i>
713 <i class="icon-flag-filled"></i>
713 <span id="todos-count">${len(c.unresolved_comments)}</span>
714 <span id="todos-count">${len(c.unresolved_comments)}</span>
714 </div>
715 </div>
715
716
716 <div class="right-sidebar-expanded-state pr-details-title">
717 <div class="right-sidebar-expanded-state pr-details-title">
717 ## Only show unresolved, that is only what matters
718 ## Only show unresolved, that is only what matters
718 <span class="sidebar-heading noselect" onclick="refreshTODOs(); return false">
719 <span class="sidebar-heading noselect" onclick="refreshTODOs(); return false">
719 <i class="icon-flag-filled"></i>
720 <i class="icon-flag-filled"></i>
720 TODOs
721 TODOs
721 </span>
722 </span>
722
723
723 % if not c.at_version:
724 % if not c.at_version:
724 % if c.resolved_comments:
725 % if c.resolved_comments:
725 <span class="block-right action_button last-item noselect" onclick="$('.unresolved-todo-text').toggle(); return toggleElement(this, '.resolved-todo');" data-toggle-on="Show resolved" data-toggle-off="Hide resolved">Show resolved</span>
726 <span class="block-right action_button last-item noselect" onclick="$('.unresolved-todo-text').toggle(); return toggleElement(this, '.resolved-todo');" data-toggle-on="Show resolved" data-toggle-off="Hide resolved">Show resolved</span>
726 % else:
727 % else:
727 <span class="block-right last-item noselect">Show resolved</span>
728 <span class="block-right last-item noselect">Show resolved</span>
728 % endif
729 % endif
729 % endif
730 % endif
730 </div>
731 </div>
731
732
732 <div class="right-sidebar-expanded-state pr-details-content">
733 <div class="right-sidebar-expanded-state pr-details-content">
733
734
734 % if c.at_version:
735 % if c.at_version:
735 <table>
736 <table>
736 <tr>
737 <tr>
737 <td class="unresolved-todo-text">${_('TODOs unavailable when browsing versions')}.</td>
738 <td class="unresolved-todo-text">${_('TODOs unavailable when browsing versions')}.</td>
738 </tr>
739 </tr>
739 </table>
740 </table>
740 % else:
741 % else:
741 % if c.unresolved_comments + c.resolved_comments:
742 % if c.unresolved_comments + c.resolved_comments:
742 ${sidebar.comments_table(c.unresolved_comments + c.resolved_comments, len(c.unresolved_comments), todo_comments=True)}
743 ${sidebar.comments_table(c.unresolved_comments + c.resolved_comments, len(c.unresolved_comments), todo_comments=True)}
743 % else:
744 % else:
744 <table class="todos-content-table">
745 <table class="todos-content-table">
745 <tr>
746 <tr>
746 <td>
747 <td>
747 ${_('No TODOs yet')}
748 ${_('No TODOs yet')}
748 </td>
749 </td>
749 </tr>
750 </tr>
750 </table>
751 </table>
751 % endif
752 % endif
752 % endif
753 % endif
753 </div>
754 </div>
754 </div>
755 </div>
755
756
756 ## COMMENTS
757 ## COMMENTS
757 <div id="commentsTable" class="sidebar-element clear-both">
758 <div id="commentsTable" class="sidebar-element clear-both">
758 <div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="${_('Comments')}">
759 <div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="${_('Comments')}">
759 <i class="icon-comment" style="color: #949494"></i>
760 <i class="icon-comment" style="color: #949494"></i>
760 <span id="comments-count">${len(c.inline_comments_flat+c.comments)}</span>
761 <span id="comments-count">${len(c.inline_comments_flat+c.comments)}</span>
761 <span class="display-none" id="general-comments-count">${len(c.comments)}</span>
762 <span class="display-none" id="general-comments-count">${len(c.comments)}</span>
762 <span class="display-none" id="inline-comments-count">${len(c.inline_comments_flat)}</span>
763 <span class="display-none" id="inline-comments-count">${len(c.inline_comments_flat)}</span>
763 </div>
764 </div>
764
765
765 <div class="right-sidebar-expanded-state pr-details-title">
766 <div class="right-sidebar-expanded-state pr-details-title">
766 <span class="sidebar-heading noselect" onclick="refreshComments(); return false">
767 <span class="sidebar-heading noselect" onclick="refreshComments(); return false">
767 <i class="icon-comment" style="color: #949494"></i>
768 <i class="icon-comment" style="color: #949494"></i>
768 ${_('Comments')}
769 ${_('Comments')}
769
770
770 ## % if outdated_comm_count_ver:
771 ## % if outdated_comm_count_ver:
771 ## <a href="#" onclick="showOutdated(); Rhodecode.comments.nextOutdatedComment(); return false;">
772 ## <a href="#" onclick="showOutdated(); Rhodecode.comments.nextOutdatedComment(); return false;">
772 ## (${_("{} Outdated").format(outdated_comm_count_ver)})
773 ## (${_("{} Outdated").format(outdated_comm_count_ver)})
773 ## </a>
774 ## </a>
774 ## <a href="#" class="showOutdatedComments" onclick="showOutdated(this); return false;"> | ${_('show outdated')}</a>
775 ## <a href="#" class="showOutdatedComments" onclick="showOutdated(this); return false;"> | ${_('show outdated')}</a>
775 ## <a href="#" class="hideOutdatedComments" style="display: none" onclick="hideOutdated(this); return false;"> | ${_('hide outdated')}</a>
776 ## <a href="#" class="hideOutdatedComments" style="display: none" onclick="hideOutdated(this); return false;"> | ${_('hide outdated')}</a>
776
777
777 ## % else:
778 ## % else:
778 ## (${_("{} Outdated").format(outdated_comm_count_ver)})
779 ## (${_("{} Outdated").format(outdated_comm_count_ver)})
779 ## % endif
780 ## % endif
780
781
781 </span>
782 </span>
782
783
783 % if outdated_comm_count_ver:
784 % if outdated_comm_count_ver:
784 <span class="block-right action_button last-item noselect" onclick="return toggleElement(this, '.hidden-comment');" data-toggle-on="Show outdated" data-toggle-off="Hide outdated">Show outdated</span>
785 <span class="block-right action_button last-item noselect" onclick="return toggleElement(this, '.hidden-comment');" data-toggle-on="Show outdated" data-toggle-off="Hide outdated">Show outdated</span>
785 % else:
786 % else:
786 <span class="block-right last-item noselect">Show hidden</span>
787 <span class="block-right last-item noselect">Show hidden</span>
787 % endif
788 % endif
788
789
789 </div>
790 </div>
790
791
791 <div class="right-sidebar-expanded-state pr-details-content">
792 <div class="right-sidebar-expanded-state pr-details-content">
792 % if c.inline_comments_flat + c.comments:
793 % if c.inline_comments_flat + c.comments:
793 ${sidebar.comments_table(c.inline_comments_flat + c.comments, len(c.inline_comments_flat+c.comments))}
794 ${sidebar.comments_table(c.inline_comments_flat + c.comments, len(c.inline_comments_flat+c.comments))}
794 % else:
795 % else:
795 <table class="comments-content-table">
796 <table class="comments-content-table">
796 <tr>
797 <tr>
797 <td>
798 <td>
798 ${_('No Comments yet')}
799 ${_('No Comments yet')}
799 </td>
800 </td>
800 </tr>
801 </tr>
801 </table>
802 </table>
802 % endif
803 % endif
803 </div>
804 </div>
804
805
805 </div>
806 </div>
806
807
807 ## Referenced Tickets
808 ## Referenced Tickets
808 <div class="sidebar-element clear-both">
809 <div class="sidebar-element clear-both">
809 <div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="${_('Referenced Tickets')}">
810 <div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="${_('Referenced Tickets')}">
810 <i class="icon-info-circled"></i>
811 <i class="icon-info-circled"></i>
811 ${(len(c.referenced_desc_issues) + len(c.referenced_commit_issues))}
812 ${(len(c.referenced_desc_issues) + len(c.referenced_commit_issues))}
812 </div>
813 </div>
813
814
814 <div class="right-sidebar-expanded-state pr-details-title">
815 <div class="right-sidebar-expanded-state pr-details-title">
815 <span class="sidebar-heading">
816 <span class="sidebar-heading">
816 <i class="icon-info-circled"></i>
817 <i class="icon-info-circled"></i>
817 ${_('Referenced Tickets')}
818 ${_('Referenced Tickets')}
818 </span>
819 </span>
819 </div>
820 </div>
820 <div class="right-sidebar-expanded-state pr-details-content">
821 <div class="right-sidebar-expanded-state pr-details-content">
821 <table>
822 <table>
822
823
823 <tr><td><code>${_('In pull request description')}:</code></td></tr>
824 <tr><td><code>${_('In pull request description')}:</code></td></tr>
824 % if c.referenced_desc_issues:
825 % if c.referenced_desc_issues:
825 % for ticket_dict in sorted(c.referenced_desc_issues):
826 % for ticket_dict in sorted(c.referenced_desc_issues):
826 <tr>
827 <tr>
827 <td>
828 <td>
828 <a href="${ticket_dict.get('url')}">
829 <a href="${ticket_dict.get('url')}">
829 ${ticket_dict.get('id')}
830 ${ticket_dict.get('id')}
830 </a>
831 </a>
831 </td>
832 </td>
832 </tr>
833 </tr>
833 % endfor
834 % endfor
834 % else:
835 % else:
835 <tr>
836 <tr>
836 <td>
837 <td>
837 ${_('No Ticket data found.')}
838 ${_('No Ticket data found.')}
838 </td>
839 </td>
839 </tr>
840 </tr>
840 % endif
841 % endif
841
842
842 <tr><td style="padding-top: 10px"><code>${_('In commit messages')}:</code></td></tr>
843 <tr><td style="padding-top: 10px"><code>${_('In commit messages')}:</code></td></tr>
843 % if c.referenced_commit_issues:
844 % if c.referenced_commit_issues:
844 % for ticket_dict in sorted(c.referenced_commit_issues):
845 % for ticket_dict in sorted(c.referenced_commit_issues):
845 <tr>
846 <tr>
846 <td>
847 <td>
847 <a href="${ticket_dict.get('url')}">
848 <a href="${ticket_dict.get('url')}">
848 ${ticket_dict.get('id')}
849 ${ticket_dict.get('id')}
849 </a>
850 </a>
850 </td>
851 </td>
851 </tr>
852 </tr>
852 % endfor
853 % endfor
853 % else:
854 % else:
854 <tr>
855 <tr>
855 <td>
856 <td>
856 ${_('No Ticket data found.')}
857 ${_('No Ticket data found.')}
857 </td>
858 </td>
858 </tr>
859 </tr>
859 % endif
860 % endif
860 </table>
861 </table>
861
862
862 </div>
863 </div>
863 </div>
864 </div>
864
865
865 </div>
866 </div>
866
867
867 </div>
868 </div>
868 </aside>
869 </aside>
869
870
870 ## This JS needs to be at the end
871 ## This JS needs to be at the end
871 <script type="text/javascript">
872 <script type="text/javascript">
872
873
873 versionController = new VersionController();
874 versionController = new VersionController();
874 versionController.init();
875 versionController.init();
875
876
876 reviewersController = new ReviewersController();
877 reviewersController = new ReviewersController();
877 commitsController = new CommitsController();
878 commitsController = new CommitsController();
878 commentsController = new CommentsController();
879 commentsController = new CommentsController();
879
880
880 updateController = new UpdatePrController();
881 updateController = new UpdatePrController();
881
882
882 window.reviewerRulesData = ${c.pull_request_default_reviewers_data_json | n};
883 window.reviewerRulesData = ${c.pull_request_default_reviewers_data_json | n};
883 window.setReviewersData = ${c.pull_request_set_reviewers_data_json | n};
884 window.setReviewersData = ${c.pull_request_set_reviewers_data_json | n};
884 window.setObserversData = ${c.pull_request_set_observers_data_json | n};
885 window.setObserversData = ${c.pull_request_set_observers_data_json | n};
885
886
886 (function () {
887 (function () {
887 "use strict";
888 "use strict";
888
889
889 // custom code mirror
890 // custom code mirror
890 var codeMirrorInstance = $('#pr-description-input').get(0).MarkupForm.cm;
891 var codeMirrorInstance = $('#pr-description-input').get(0).MarkupForm.cm;
891
892
892 PRDetails.init();
893 PRDetails.init();
893 ReviewersPanel.init(reviewersController, reviewerRulesData, setReviewersData);
894 ReviewersPanel.init(reviewersController, reviewerRulesData, setReviewersData);
894 ObserversPanel.init(reviewersController, reviewerRulesData, setObserversData);
895 ObserversPanel.init(reviewersController, reviewerRulesData, setObserversData);
895
896
896 window.showOutdated = function (self) {
897 window.showOutdated = function (self) {
897 $('.comment-inline.comment-outdated').show();
898 $('.comment-inline.comment-outdated').show();
898 $('.filediff-outdated').show();
899 $('.filediff-outdated').show();
899 $('.showOutdatedComments').hide();
900 $('.showOutdatedComments').hide();
900 $('.hideOutdatedComments').show();
901 $('.hideOutdatedComments').show();
901 };
902 };
902
903
903 window.hideOutdated = function (self) {
904 window.hideOutdated = function (self) {
904 $('.comment-inline.comment-outdated').hide();
905 $('.comment-inline.comment-outdated').hide();
905 $('.filediff-outdated').hide();
906 $('.filediff-outdated').hide();
906 $('.hideOutdatedComments').hide();
907 $('.hideOutdatedComments').hide();
907 $('.showOutdatedComments').show();
908 $('.showOutdatedComments').show();
908 };
909 };
909
910
910 window.refreshMergeChecks = function () {
911 window.refreshMergeChecks = function () {
911 var loadUrl = "${request.current_route_path(_query=dict(merge_checks=1))}";
912 var loadUrl = "${request.current_route_path(_query=dict(merge_checks=1))}";
912 $('.pull-request-merge').css('opacity', 0.3);
913 $('.pull-request-merge').css('opacity', 0.3);
913 $('.action-buttons-extra').css('opacity', 0.3);
914 $('.action-buttons-extra').css('opacity', 0.3);
914
915
915 $('.pull-request-merge').load(
916 $('.pull-request-merge').load(
916 loadUrl, function () {
917 loadUrl, function () {
917 $('.pull-request-merge').css('opacity', 1);
918 $('.pull-request-merge').css('opacity', 1);
918
919
919 $('.action-buttons-extra').css('opacity', 1);
920 $('.action-buttons-extra').css('opacity', 1);
920 }
921 }
921 );
922 );
922 };
923 };
923
924
924 window.submitDrafts = function (event) {
925 window.submitDrafts = function (event) {
925 var target = $(event.currentTarget);
926 var target = $(event.currentTarget);
926 var callback = function (result) {
927 var callback = function (result) {
927 target.removeAttr('onclick').html('saving...');
928 target.removeAttr('onclick').html('saving...');
928 }
929 }
929 var draftIds = [];
930 var draftIds = [];
930 $.each($('[name=submit_draft]:checked'), function (idx, val) {
931 $.each($('[name=submit_draft]:checked'), function (idx, val) {
931 draftIds.push(parseInt($(val).val()));
932 draftIds.push(parseInt($(val).val()));
932 })
933 })
933 if (draftIds.length > 0) {
934 if (draftIds.length > 0) {
934 Rhodecode.comments.finalizeDrafts(draftIds, callback);
935 Rhodecode.comments.finalizeDrafts(draftIds, callback);
935 }
936 }
936 else {
937 else {
937
938
938 }
939 }
939 }
940 }
940
941
941 window.closePullRequest = function (status) {
942 window.closePullRequest = function (status) {
942 if (!confirm(_gettext('Are you sure to close this pull request without merging?'))) {
943 if (!confirm(_gettext('Are you sure to close this pull request without merging?'))) {
943 return false;
944 return false;
944 }
945 }
945 // inject closing flag
946 // inject closing flag
946 $('.action-buttons-extra').append('<input type="hidden" class="close-pr-input" id="close_pull_request" value="1">');
947 $('.action-buttons-extra').append('<input type="hidden" class="close-pr-input" id="close_pull_request" value="1">');
947 $(generalCommentForm.statusChange).select2("val", status).trigger('change');
948 $(generalCommentForm.statusChange).select2("val", status).trigger('change');
948 $(generalCommentForm.submitForm).submit();
949 $(generalCommentForm.submitForm).submit();
949 };
950 };
950
951
951 //TODO this functionality is now missing
952 //TODO this functionality is now missing
952 $('#show-outdated-comments').on('click', function (e) {
953 $('#show-outdated-comments').on('click', function (e) {
953 var button = $(this);
954 var button = $(this);
954 var outdated = $('.comment-outdated');
955 var outdated = $('.comment-outdated');
955
956
956 if (button.html() === "(Show)") {
957 if (button.html() === "(Show)") {
957 button.html("(Hide)");
958 button.html("(Hide)");
958 outdated.show();
959 outdated.show();
959 } else {
960 } else {
960 button.html("(Show)");
961 button.html("(Show)");
961 outdated.hide();
962 outdated.hide();
962 }
963 }
963 });
964 });
964
965
965 $('#merge_pull_request_form').submit(function () {
966 $('#merge_pull_request_form').submit(function () {
966 if (!$('#merge_pull_request').attr('disabled')) {
967 if (!$('#merge_pull_request').attr('disabled')) {
967 $('#merge_pull_request').attr('disabled', 'disabled');
968 $('#merge_pull_request').attr('disabled', 'disabled');
968 }
969 }
969 return true;
970 return true;
970 });
971 });
971
972
972 $('#edit_pull_request').on('click', function (e) {
973 $('#edit_pull_request').on('click', function (e) {
973 var title = $('#pr-title-input').val();
974 var title = $('#pr-title-input').val();
974 var description = codeMirrorInstance.getValue();
975 var description = codeMirrorInstance.getValue();
975 var renderer = $('#pr-renderer-input').val();
976 var renderer = $('#pr-renderer-input').val();
976 editPullRequest(
977 editPullRequest(
977 "${c.repo_name}", "${c.pull_request.pull_request_id}",
978 "${c.repo_name}", "${c.pull_request.pull_request_id}",
978 title, description, renderer);
979 title, description, renderer);
979 });
980 });
980
981
981 var $updateButtons = $('#update_reviewers,#update_observers');
982 var $updateButtons = $('#update_reviewers,#update_observers');
982 $updateButtons.on('click', function (e) {
983 $updateButtons.on('click', function (e) {
983 var role = $(this).data('role');
984 var role = $(this).data('role');
984 $updateButtons.attr('disabled', 'disabled');
985 $updateButtons.attr('disabled', 'disabled');
985 $updateButtons.addClass('disabled');
986 $updateButtons.addClass('disabled');
986 $updateButtons.html(_gettext('Saving...'));
987 $updateButtons.html(_gettext('Saving...'));
987 reviewersController.updateReviewers(
988 reviewersController.updateReviewers(
988 templateContext.repo_name,
989 templateContext.repo_name,
989 templateContext.pull_request_data.pull_request_id,
990 templateContext.pull_request_data.pull_request_id,
990 role
991 role
991 );
992 );
992 });
993 });
993
994
994 // fixing issue with caches on firefox
995 // fixing issue with caches on firefox
995 $('#update_commits').removeAttr("disabled");
996 $('#update_commits').removeAttr("disabled");
996
997
997 $('.show-inline-comments').on('click', function (e) {
998 $('.show-inline-comments').on('click', function (e) {
998 var boxid = $(this).attr('data-comment-id');
999 var boxid = $(this).attr('data-comment-id');
999 var button = $(this);
1000 var button = $(this);
1000
1001
1001 if (button.hasClass("comments-visible")) {
1002 if (button.hasClass("comments-visible")) {
1002 $('#{0} .inline-comments'.format(boxid)).each(function (index) {
1003 $('#{0} .inline-comments'.format(boxid)).each(function (index) {
1003 $(this).hide();
1004 $(this).hide();
1004 });
1005 });
1005 button.removeClass("comments-visible");
1006 button.removeClass("comments-visible");
1006 } else {
1007 } else {
1007 $('#{0} .inline-comments'.format(boxid)).each(function (index) {
1008 $('#{0} .inline-comments'.format(boxid)).each(function (index) {
1008 $(this).show();
1009 $(this).show();
1009 });
1010 });
1010 button.addClass("comments-visible");
1011 button.addClass("comments-visible");
1011 }
1012 }
1012 });
1013 });
1013
1014
1014 $('.show-inline-comments').on('change', function (e) {
1015 $('.show-inline-comments').on('change', function (e) {
1015 var show = 'none';
1016 var show = 'none';
1016 var target = e.currentTarget;
1017 var target = e.currentTarget;
1017 if (target.checked) {
1018 if (target.checked) {
1018 show = ''
1019 show = ''
1019 }
1020 }
1020 var boxid = $(target).attr('id_for');
1021 var boxid = $(target).attr('id_for');
1021 var comments = $('#{0} .inline-comments'.format(boxid));
1022 var comments = $('#{0} .inline-comments'.format(boxid));
1022 var fn_display = function (idx) {
1023 var fn_display = function (idx) {
1023 $(this).css('display', show);
1024 $(this).css('display', show);
1024 };
1025 };
1025 $(comments).each(fn_display);
1026 $(comments).each(fn_display);
1026 var btns = $('#{0} .inline-comments-button'.format(boxid));
1027 var btns = $('#{0} .inline-comments-button'.format(boxid));
1027 $(btns).each(fn_display);
1028 $(btns).each(fn_display);
1028 });
1029 });
1029
1030
1030 // register submit callback on commentForm form to track TODOs, and refresh mergeChecks conditions
1031 // register submit callback on commentForm form to track TODOs, and refresh mergeChecks conditions
1031 window.commentFormGlobalSubmitSuccessCallback = function (comment) {
1032 window.commentFormGlobalSubmitSuccessCallback = function (comment) {
1032 if (!comment.draft) {
1033 if (!comment.draft) {
1033 refreshMergeChecks();
1034 refreshMergeChecks();
1034 }
1035 }
1035 };
1036 };
1036
1037
1037 ReviewerAutoComplete('#user', reviewersController);
1038 ReviewerAutoComplete('#user', reviewersController);
1038 ObserverAutoComplete('#observer', reviewersController);
1039 ObserverAutoComplete('#observer', reviewersController);
1039
1040
1040 })();
1041 })();
1041
1042
1042 $(document).ready(function () {
1043 $(document).ready(function () {
1043
1044
1044 var channel = '${c.pr_broadcast_channel}';
1045 var channel = '${c.pr_broadcast_channel}';
1045 new ReviewerPresenceController(channel)
1046 new ReviewerPresenceController(channel)
1046 // register globally so inject comment logic can re-use it.
1047 // register globally so inject comment logic can re-use it.
1047 window.commentsController = commentsController;
1048 window.commentsController = commentsController;
1048 })
1049 })
1049 </script>
1050 </script>
1050
1051
1051 </%def>
1052 </%def>
General Comments 0
You need to be logged in to leave comments. Login now