##// END OF EJS Templates
ux: add hidden comments indicators plus style tweaks...
dan -
r1157:ac063abc default
parent child Browse files
Show More
@@ -1,1142 +1,1167 b''
1 1 // Default styles
2 2
3 3 .diff-collapse {
4 4 margin: @padding 0;
5 5 text-align: right;
6 6 }
7 7
8 8 .diff-container {
9 9 margin-bottom: @space;
10 10
11 11 .diffblock {
12 12 margin-bottom: @space;
13 13 }
14 14
15 15 &.hidden {
16 16 display: none;
17 17 overflow: hidden;
18 18 }
19 19 }
20 20
21 21 .compare_view_files {
22 22
23 23 .diff-container {
24 24
25 25 .diffblock {
26 26 margin-bottom: 0;
27 27 }
28 28 }
29 29 }
30 30
31 31 div.diffblock .sidebyside {
32 32 background: #ffffff;
33 33 }
34 34
35 35 div.diffblock {
36 36 overflow-x: auto;
37 37 overflow-y: hidden;
38 38 clear: both;
39 39 padding: 0px;
40 40 background: @grey6;
41 41 border: @border-thickness solid @grey5;
42 42 -webkit-border-radius: @border-radius @border-radius 0px 0px;
43 43 border-radius: @border-radius @border-radius 0px 0px;
44 44
45 45
46 46 .comments-number {
47 47 float: right;
48 48 }
49 49
50 50 // BEGIN CODE-HEADER STYLES
51 51
52 52 .code-header {
53 53 background: @grey6;
54 54 padding: 10px 0 10px 0;
55 55 height: auto;
56 56 width: 100%;
57 57
58 58 .hash {
59 59 float: left;
60 60 padding: 2px 0 0 2px;
61 61 }
62 62
63 63 .date {
64 64 float: left;
65 65 text-transform: uppercase;
66 66 padding: 4px 0px 0px 2px;
67 67 }
68 68
69 69 div {
70 70 margin-left: 4px;
71 71 }
72 72
73 73 div.compare_header {
74 74 min-height: 40px;
75 75 margin: 0;
76 76 padding: 0 @padding;
77 77
78 78 .drop-menu {
79 79 float:left;
80 80 display: block;
81 81 margin:0 0 @padding 0;
82 82 }
83 83
84 84 .compare-label {
85 85 float: left;
86 86 clear: both;
87 87 display: inline-block;
88 88 min-width: 5em;
89 89 margin: 0;
90 90 padding: @button-padding @button-padding @button-padding 0;
91 91 font-family: @text-semibold;
92 92 }
93 93
94 94 .compare-buttons {
95 95 float: left;
96 96 margin: 0;
97 97 padding: 0 0 @padding;
98 98
99 99 .btn {
100 100 margin: 0 @padding 0 0;
101 101 }
102 102 }
103 103 }
104 104
105 105 }
106 106
107 107 .parents {
108 108 float: left;
109 109 width: 100px;
110 110 font-weight: 400;
111 111 vertical-align: middle;
112 112 padding: 0px 2px 0px 2px;
113 113 background-color: @grey6;
114 114
115 115 #parent_link {
116 116 margin: 00px 2px;
117 117
118 118 &.double {
119 119 margin: 0px 2px;
120 120 }
121 121
122 122 &.disabled{
123 123 margin-right: @padding;
124 124 }
125 125 }
126 126 }
127 127
128 128 .children {
129 129 float: right;
130 130 width: 100px;
131 131 font-weight: 400;
132 132 vertical-align: middle;
133 133 text-align: right;
134 134 padding: 0px 2px 0px 2px;
135 135 background-color: @grey6;
136 136
137 137 #child_link {
138 138 margin: 0px 2px;
139 139
140 140 &.double {
141 141 margin: 0px 2px;
142 142 }
143 143
144 144 &.disabled{
145 145 margin-right: @padding;
146 146 }
147 147 }
148 148 }
149 149
150 150 .changeset_header {
151 151 height: 16px;
152 152
153 153 & > div{
154 154 margin-right: @padding;
155 155 }
156 156 }
157 157
158 158 .changeset_file {
159 159 text-align: left;
160 160 float: left;
161 161 padding: 0;
162 162
163 163 a{
164 164 display: inline-block;
165 165 margin-right: 0.5em;
166 166 }
167 167
168 168 #selected_mode{
169 169 margin-left: 0;
170 170 }
171 171 }
172 172
173 173 .diff-menu-wrapper {
174 174 float: left;
175 175 }
176 176
177 177 .diff-menu {
178 178 position: absolute;
179 179 background: none repeat scroll 0 0 #FFFFFF;
180 180 border-color: #003367 @grey3 @grey3;
181 181 border-right: 1px solid @grey3;
182 182 border-style: solid solid solid;
183 183 border-width: @border-thickness;
184 184 box-shadow: 2px 8px 4px rgba(0, 0, 0, 0.2);
185 185 margin-top: 5px;
186 186 margin-left: 1px;
187 187 }
188 188
189 189 .diff-actions, .editor-actions {
190 190 float: left;
191 191
192 192 input{
193 193 margin: 0 0.5em 0 0;
194 194 }
195 195 }
196 196
197 197 // END CODE-HEADER STYLES
198 198
199 199 // BEGIN CODE-BODY STYLES
200 200
201 201 .code-body {
202 202 background: white;
203 203 padding: 0;
204 204 background-color: #ffffff;
205 205 position: relative;
206 206 max-width: none;
207 207 box-sizing: border-box;
208 208 // TODO: johbo: Parent has overflow: auto, this forces the child here
209 209 // to have the intended size and to scroll. Should be simplified.
210 210 width: 100%;
211 211 overflow-x: auto;
212 212 }
213 213
214 214 pre.raw {
215 215 background: white;
216 216 color: @grey1;
217 217 }
218 218 // END CODE-BODY STYLES
219 219
220 220 }
221 221
222 222
223 223 table.code-difftable {
224 224 border-collapse: collapse;
225 225 width: 99%;
226 226 border-radius: 0px !important;
227 227
228 228 td {
229 229 padding: 0 !important;
230 230 background: none !important;
231 231 border: 0 !important;
232 232 }
233 233
234 234 .context {
235 235 background: none repeat scroll 0 0 #DDE7EF;
236 236 }
237 237
238 238 .add {
239 239 background: none repeat scroll 0 0 #DDFFDD;
240 240
241 241 ins {
242 242 background: none repeat scroll 0 0 #AAFFAA;
243 243 text-decoration: none;
244 244 }
245 245 }
246 246
247 247 .del {
248 248 background: none repeat scroll 0 0 #FFDDDD;
249 249
250 250 del {
251 251 background: none repeat scroll 0 0 #FFAAAA;
252 252 text-decoration: none;
253 253 }
254 254 }
255 255
256 256 /** LINE NUMBERS **/
257 257 .lineno {
258 258 padding-left: 2px !important;
259 259 padding-right: 2px;
260 260 text-align: right;
261 261 width: 32px;
262 262 -moz-user-select: none;
263 263 -webkit-user-select: none;
264 264 border-right: @border-thickness solid @grey5 !important;
265 265 border-left: 0px solid #CCC !important;
266 266 border-top: 0px solid #CCC !important;
267 267 border-bottom: none !important;
268 268
269 269 a {
270 270 &:extend(pre);
271 271 text-align: right;
272 272 padding-right: 2px;
273 273 cursor: pointer;
274 274 display: block;
275 275 width: 32px;
276 276 }
277 277 }
278 278
279 279 .context {
280 280 cursor: auto;
281 281 &:extend(pre);
282 282 }
283 283
284 284 .lineno-inline {
285 285 background: none repeat scroll 0 0 #FFF !important;
286 286 padding-left: 2px;
287 287 padding-right: 2px;
288 288 text-align: right;
289 289 width: 30px;
290 290 -moz-user-select: none;
291 291 -webkit-user-select: none;
292 292 }
293 293
294 294 /** CODE **/
295 295 .code {
296 296 display: block;
297 297 width: 100%;
298 298
299 299 td {
300 300 margin: 0;
301 301 padding: 0;
302 302 }
303 303
304 304 pre {
305 305 margin: 0;
306 306 padding: 0;
307 307 margin-left: .5em;
308 308 }
309 309 }
310 310 }
311 311
312 312
313 313 // Comments
314 314
315 315 div.comment:target {
316 316 border-left: 6px solid @comment-highlight-color;
317 317 padding-left: 3px;
318 318 margin-left: -9px;
319 319 }
320 320
321 321 //TODO: anderson: can't get an absolute number out of anything, so had to put the
322 322 //current values that might change. But to make it clear I put as a calculation
323 323 @comment-max-width: 1065px;
324 324 @pr-extra-margin: 34px;
325 325 @pr-border-spacing: 4px;
326 326 @pr-comment-width: @comment-max-width - @pr-extra-margin - @pr-border-spacing;
327 327
328 328 // Pull Request
329 329 .cs_files .code-difftable {
330 330 border: @border-thickness solid @grey5; //borders only on PRs
331 331
332 332 .comment-inline-form,
333 333 div.comment {
334 334 width: @pr-comment-width;
335 335 }
336 336 }
337 337
338 338 // Changeset
339 339 .code-difftable {
340 340 .comment-inline-form,
341 341 div.comment {
342 342 width: @comment-max-width;
343 343 }
344 344 }
345 345
346 346 //Style page
347 347 @style-extra-margin: @sidebar-width + (@sidebarpadding * 3) + @padding;
348 348 #style-page .code-difftable{
349 349 .comment-inline-form,
350 350 div.comment {
351 351 width: @comment-max-width - @style-extra-margin;
352 352 }
353 353 }
354 354
355 355 #context-bar > h2 {
356 356 font-size: 20px;
357 357 }
358 358
359 359 #context-bar > h2> a {
360 360 font-size: 20px;
361 361 }
362 362 // end of defaults
363 363
364 364 .file_diff_buttons {
365 365 padding: 0 0 @padding;
366 366
367 367 .drop-menu {
368 368 float: left;
369 369 margin: 0 @padding 0 0;
370 370 }
371 371 .btn {
372 372 margin: 0 @padding 0 0;
373 373 }
374 374 }
375 375
376 376 .code-body.textarea.editor {
377 377 max-width: none;
378 378 padding: 15px;
379 379 }
380 380
381 381 td.injected_diff{
382 382 max-width: 1178px;
383 383 overflow-x: auto;
384 384 overflow-y: hidden;
385 385
386 386 div.diff-container,
387 387 div.diffblock{
388 388 max-width: 100%;
389 389 }
390 390
391 391 div.code-body {
392 392 max-width: 1124px;
393 393 overflow-x: auto;
394 394 overflow-y: hidden;
395 395 padding: 0;
396 396 }
397 397 div.diffblock {
398 398 border: none;
399 399 }
400 400
401 401 &.inline-form {
402 402 width: 99%
403 403 }
404 404 }
405 405
406 406
407 407 table.code-difftable {
408 408 width: 100%;
409 409 }
410 410
411 411 /** PYGMENTS COLORING **/
412 412 div.codeblock {
413 413
414 414 // TODO: johbo: Added interim to get rid of the margin around
415 415 // Select2 widgets. This needs further cleanup.
416 416 margin-top: @padding;
417 417
418 418 overflow: auto;
419 419 padding: 0px;
420 420 border: @border-thickness solid @grey5;
421 421 background: @grey6;
422 422 .border-radius(@border-radius);
423 423
424 424 #remove_gist {
425 425 float: right;
426 426 }
427 427
428 428 .author {
429 429 clear: both;
430 430 vertical-align: middle;
431 431 font-family: @text-bold;
432 432 }
433 433
434 434 .btn-mini {
435 435 float: left;
436 436 margin: 0 5px 0 0;
437 437 }
438 438
439 439 .code-header {
440 440 padding: @padding;
441 441 border-bottom: @border-thickness solid @grey5;
442 442
443 443 .rc-user {
444 444 min-width: 0;
445 445 margin-right: .5em;
446 446 }
447 447
448 448 .stats {
449 449 clear: both;
450 450 margin: 0 0 @padding 0;
451 451 padding: 0;
452 452 .left {
453 453 float: left;
454 454 clear: left;
455 455 max-width: 75%;
456 456 margin: 0 0 @padding 0;
457 457
458 458 &.item {
459 459 margin-right: @padding;
460 460 &.last { border-right: none; }
461 461 }
462 462 }
463 463 .buttons { float: right; }
464 464 .author {
465 465 height: 25px; margin-left: 15px; font-weight: bold;
466 466 }
467 467 }
468 468
469 469 .commit {
470 470 margin: 5px 0 0 26px;
471 471 font-weight: normal;
472 472 white-space: pre-wrap;
473 473 }
474 474 }
475 475
476 476 .message {
477 477 position: relative;
478 478 margin: @padding;
479 479
480 480 .codeblock-label {
481 481 margin: 0 0 1em 0;
482 482 }
483 483 }
484 484
485 485 .code-body {
486 486 padding: @padding;
487 487 background-color: #ffffff;
488 488 min-width: 100%;
489 489 box-sizing: border-box;
490 490 // TODO: johbo: Parent has overflow: auto, this forces the child here
491 491 // to have the intended size and to scroll. Should be simplified.
492 492 width: 100%;
493 493 overflow-x: auto;
494 494 }
495 495 }
496 496
497 497 .code-highlighttable,
498 498 div.codeblock {
499 499
500 500 &.readme {
501 501 background-color: white;
502 502 }
503 503
504 504 .markdown-block table {
505 505 border-collapse: collapse;
506 506
507 507 th,
508 508 td {
509 509 padding: .5em;
510 510 border: @border-thickness solid @border-default-color;
511 511 }
512 512 }
513 513
514 514 table {
515 515 border: 0px;
516 516 margin: 0;
517 517 letter-spacing: normal;
518 518
519 519
520 520 td {
521 521 border: 0px;
522 522 vertical-align: top;
523 523 }
524 524 }
525 525 }
526 526
527 527 div.codeblock .code-header .search-path { padding: 0 0 0 10px; }
528 528 div.search-code-body {
529 529 background-color: #ffffff; padding: 5px 0 5px 10px;
530 530 pre {
531 531 .match { background-color: #faffa6;}
532 532 .break { display: block; width: 100%; background-color: #DDE7EF; color: #747474; }
533 533 }
534 534 .code-highlighttable {
535 535 border-collapse: collapse;
536 536
537 537 tr:hover {
538 538 background: #fafafa;
539 539 }
540 540 td.code {
541 541 padding-left: 10px;
542 542 }
543 543 td.line {
544 544 border-right: 1px solid #ccc !important;
545 545 padding-right: 10px;
546 546 text-align: right;
547 547 font-family: "Lucida Console",Monaco,monospace;
548 548 span {
549 549 white-space: pre-wrap;
550 550 color: #666666;
551 551 }
552 552 }
553 553 }
554 554 }
555 555
556 556 div.annotatediv { margin-left: 2px; margin-right: 4px; }
557 557 .code-highlight {
558 558 margin: 0; padding: 0; border-left: @border-thickness solid @grey5;
559 559 pre, .linenodiv pre { padding: 0 5px; margin: 0; }
560 560 pre div:target {background-color: @comment-highlight-color !important;}
561 561 }
562 562
563 563 .linenos a { text-decoration: none; }
564 564
565 565 .CodeMirror-selected { background: @rchighlightblue; }
566 566 .CodeMirror-focused .CodeMirror-selected { background: @rchighlightblue; }
567 567 .CodeMirror ::selection { background: @rchighlightblue; }
568 568 .CodeMirror ::-moz-selection { background: @rchighlightblue; }
569 569
570 570 .code { display: block; border:0px !important; }
571 571 .code-highlight, /* TODO: dan: merge codehilite into code-highlight */
572 572 .codehilite {
573 573 .hll { background-color: #ffffcc }
574 574 .c { color: #408080; font-style: italic } /* Comment */
575 575 .err, .codehilite .err { border: @border-thickness solid #FF0000 } /* Error */
576 576 .k { color: #008000; font-weight: bold } /* Keyword */
577 577 .o { color: #666666 } /* Operator */
578 578 .cm { color: #408080; font-style: italic } /* Comment.Multiline */
579 579 .cp { color: #BC7A00 } /* Comment.Preproc */
580 580 .c1 { color: #408080; font-style: italic } /* Comment.Single */
581 581 .cs { color: #408080; font-style: italic } /* Comment.Special */
582 582 .gd { color: #A00000 } /* Generic.Deleted */
583 583 .ge { font-style: italic } /* Generic.Emph */
584 584 .gr { color: #FF0000 } /* Generic.Error */
585 585 .gh { color: #000080; font-weight: bold } /* Generic.Heading */
586 586 .gi { color: #00A000 } /* Generic.Inserted */
587 587 .go { color: #808080 } /* Generic.Output */
588 588 .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
589 589 .gs { font-weight: bold } /* Generic.Strong */
590 590 .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
591 591 .gt { color: #0040D0 } /* Generic.Traceback */
592 592 .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
593 593 .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
594 594 .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
595 595 .kp { color: #008000 } /* Keyword.Pseudo */
596 596 .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
597 597 .kt { color: #B00040 } /* Keyword.Type */
598 598 .m { color: #666666 } /* Literal.Number */
599 599 .s { color: #BA2121 } /* Literal.String */
600 600 .na { color: #7D9029 } /* Name.Attribute */
601 601 .nb { color: #008000 } /* Name.Builtin */
602 602 .nc { color: #0000FF; font-weight: bold } /* Name.Class */
603 603 .no { color: #880000 } /* Name.Constant */
604 604 .nd { color: #AA22FF } /* Name.Decorator */
605 605 .ni { color: #999999; font-weight: bold } /* Name.Entity */
606 606 .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
607 607 .nf { color: #0000FF } /* Name.Function */
608 608 .nl { color: #A0A000 } /* Name.Label */
609 609 .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
610 610 .nt { color: #008000; font-weight: bold } /* Name.Tag */
611 611 .nv { color: #19177C } /* Name.Variable */
612 612 .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
613 613 .w { color: #bbbbbb } /* Text.Whitespace */
614 614 .mf { color: #666666 } /* Literal.Number.Float */
615 615 .mh { color: #666666 } /* Literal.Number.Hex */
616 616 .mi { color: #666666 } /* Literal.Number.Integer */
617 617 .mo { color: #666666 } /* Literal.Number.Oct */
618 618 .sb { color: #BA2121 } /* Literal.String.Backtick */
619 619 .sc { color: #BA2121 } /* Literal.String.Char */
620 620 .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
621 621 .s2 { color: #BA2121 } /* Literal.String.Double */
622 622 .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
623 623 .sh { color: #BA2121 } /* Literal.String.Heredoc */
624 624 .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
625 625 .sx { color: #008000 } /* Literal.String.Other */
626 626 .sr { color: #BB6688 } /* Literal.String.Regex */
627 627 .s1 { color: #BA2121 } /* Literal.String.Single */
628 628 .ss { color: #19177C } /* Literal.String.Symbol */
629 629 .bp { color: #008000 } /* Name.Builtin.Pseudo */
630 630 .vc { color: #19177C } /* Name.Variable.Class */
631 631 .vg { color: #19177C } /* Name.Variable.Global */
632 632 .vi { color: #19177C } /* Name.Variable.Instance */
633 633 .il { color: #666666 } /* Literal.Number.Integer.Long */
634 634 }
635 635
636 636 /* customized pre blocks for markdown/rst */
637 637 pre.literal-block, .codehilite pre{
638 638 padding: @padding;
639 639 border: 1px solid @grey6;
640 640 .border-radius(@border-radius);
641 641 background-color: @grey7;
642 642 }
643 643
644 644
645 645 /* START NEW CODE BLOCK CSS */
646 646
647 647 @cb-line-height: 18px;
648 648 @cb-line-code-padding: 10px;
649 649 @cb-text-padding: 5px;
650 650
651 651 @pill-padding: 2px 7px;
652 652
653 653 input.filediff-collapse-state {
654 654 display: none;
655 655
656 656 &:checked + .filediff { /* file diff is collapsed */
657 657 .cb {
658 658 display: none
659 659 }
660 660 .filediff-collapse-indicator {
661 661 border-width: 9px 0 9px 15.6px;
662 662 border-color: transparent transparent transparent #ccc;
663 663 }
664 664 .filediff-menu {
665 665 display: none;
666 666 }
667 667 margin: -1px 0 0 0;
668 668 }
669 669
670 670 &+ .filediff { /* file diff is expanded */
671 671 .filediff-collapse-indicator {
672 672 border-width: 15.6px 9px 0 9px;
673 673 border-color: #ccc transparent transparent transparent;
674 674 }
675 675 .filediff-menu {
676 676 display: block;
677 677 }
678 678 margin: 20px 0;
679 679 &:nth-child(2) {
680 680 margin: 0;
681 681 }
682 682 }
683 683 }
684 684 .cs_files {
685 685 clear: both;
686 686 }
687 687
688 688 .diffset-menu {
689 689 margin-bottom: 20px;
690 690 }
691 691 .diffset {
692 692 margin: 20px auto;
693 693 .diffset-heading {
694 694 border: 1px solid @grey5;
695 695 margin-bottom: -1px;
696 696 // margin-top: 20px;
697 697 h2 {
698 698 margin: 0;
699 699 line-height: 38px;
700 700 padding-left: 10px;
701 701 }
702 702 .btn {
703 703 margin: 0;
704 704 }
705 705 background: @grey6;
706 706 display: block;
707 707 padding: 5px;
708 708 }
709 709 .diffset-heading-warning {
710 710 background: @alert3-inner;
711 711 border: 1px solid @alert3;
712 712 }
713 713 }
714 714 .pill {
715 715 display: block;
716 716 float: left;
717 717 padding: @pill-padding;
718 718 }
719 719 .pill-group {
720 720 .pill {
721 721 opacity: .8;
722 722 &:first-child {
723 723 border-radius: @border-radius 0 0 @border-radius;
724 724 }
725 725 &:last-child {
726 726 border-radius: 0 @border-radius @border-radius 0;
727 727 }
728 728 &:only-child {
729 729 border-radius: @border-radius;
730 730 }
731 731 }
732 732 }
733 733
734 734 .filediff {
735 735 border: 1px solid @grey5;
736 736
737 737 /* START OVERRIDES */
738 738 .code-highlight {
739 739 border: none; // TODO: remove this border from the global
740 740 // .code-highlight, it doesn't belong there
741 741 }
742 742 label {
743 743 margin: 0; // TODO: remove this margin definition from global label
744 744 // it doesn't belong there - if margin on labels
745 745 // are needed for a form they should be defined
746 746 // in the form's class
747 747 }
748 748 /* END OVERRIDES */
749 749
750 750 * {
751 751 box-sizing: border-box;
752 752 }
753 753 .filediff-anchor {
754 754 visibility: hidden;
755 755 }
756 756 &:hover {
757 757 .filediff-anchor {
758 758 visibility: visible;
759 759 }
760 760 }
761 761
762 762 .filediff-collapse-indicator {
763 763 width: 0;
764 764 height: 0;
765 765 border-style: solid;
766 766 float: left;
767 767 margin: 2px 2px 0 0;
768 768 cursor: pointer;
769 769 }
770 770
771 771 .filediff-heading {
772 772 background: @grey7;
773 773 cursor: pointer;
774 774 display: block;
775 775 padding: 5px 10px;
776 776 }
777 777 .filediff-heading:after {
778 778 content: "";
779 779 display: table;
780 780 clear: both;
781 781 }
782 782 .filediff-heading:hover {
783 783 background: #e1e9f4 !important;
784 784 }
785 785
786 786 .filediff-menu {
787 787 float: right;
788 788
789 789 &> a, &> span {
790 790 padding: 5px;
791 791 display: block;
792 792 float: left
793 793 }
794 794 }
795 795
796 796 .pill {
797 797 &[op="name"] {
798 798 background: none;
799 799 color: @grey2;
800 800 opacity: 1;
801 801 color: white;
802 802 }
803 803 &[op="limited"] {
804 804 background: @grey2;
805 805 color: white;
806 806 }
807 807 &[op="binary"] {
808 808 background: @color7;
809 809 color: white;
810 810 }
811 811 &[op="modified"] {
812 812 background: @alert1;
813 813 color: white;
814 814 }
815 815 &[op="renamed"] {
816 816 background: @color4;
817 817 color: white;
818 818 }
819 819 &[op="mode"] {
820 820 background: @grey3;
821 821 color: white;
822 822 }
823 823 &[op="symlink"] {
824 824 background: @color8;
825 825 color: white;
826 826 }
827 827
828 828 &[op="added"] { /* added lines */
829 829 background: @alert1;
830 830 color: white;
831 831 }
832 832 &[op="deleted"] { /* deleted lines */
833 833 background: @alert2;
834 834 color: white;
835 835 }
836 836
837 837 &[op="created"] { /* created file */
838 838 background: @alert1;
839 839 color: white;
840 840 }
841 841 &[op="removed"] { /* deleted file */
842 842 background: @color5;
843 843 color: white;
844 844 }
845 845 }
846 846
847 847 .filediff-collapse-button, .filediff-expand-button {
848 848 cursor: pointer;
849 849 }
850 850 .filediff-collapse-button {
851 851 display: inline;
852 852 }
853 853 .filediff-expand-button {
854 854 display: none;
855 855 }
856 856 .filediff-collapsed .filediff-collapse-button {
857 857 display: none;
858 858 }
859 859 .filediff-collapsed .filediff-expand-button {
860 860 display: inline;
861 861 }
862 862
863 863 @comment-padding: 5px;
864 864
865 865 /**** COMMENTS ****/
866 866
867 867 .filediff-menu {
868 868 .show-comment-button {
869 869 display: none;
870 870 }
871 871 }
872 872 &.hide-comments {
873 873 .inline-comments {
874 874 display: none;
875 875 }
876 876 .filediff-menu {
877 877 .show-comment-button {
878 878 display: inline;
879 879 }
880 .show-comment-button {
880 .hide-comment-button {
881 881 display: none;
882 882 }
883 883 }
884 884 }
885
886 .hide-line-comments {
887 .inline-comments {
888 display: none;
889 }
890 }
885 891 .inline-comments {
886 892 border-radius: @border-radius;
887 893 background: @grey6;
888 894 .comment {
889 895 margin: 0;
890 896 border-radius: @border-radius;
891 897 }
892 898 .comment-outdated {
893 899 opacity: 0.5;
894 900 }
895 901 .comment-inline {
896 902 background: white;
897 903 padding: (@comment-padding + 3px) @comment-padding;
898 904 border: @comment-padding solid @grey6;
899 905
900 906 .text {
901 907 border: none;
902 908 }
903 909 .meta {
904 910 border-bottom: 1px solid @grey6;
905 911 padding-bottom: 10px;
906 912 }
907 913 }
908 914 .comment-selected {
909 915 border-left: 6px solid @comment-highlight-color;
910 916 }
911 917 .comment-inline-form {
912 918 padding: @comment-padding;
913 919 display: none;
914 920 }
915 921 .cb-comment-add-button {
916 922 margin: @comment-padding;
917 923 }
918 924 /* hide add comment button when form is open */
919 925 .comment-inline-form-open + .cb-comment-add-button {
920 926 display: none;
921 927 }
922 928 .comment-inline-form-open {
923 929 display: block;
924 930 }
925 931 /* hide add comment button when form but no comments */
926 932 .comment-inline-form:first-child + .cb-comment-add-button {
927 933 display: none;
928 934 }
929 935 /* hide add comment button when no comments or form */
930 936 .cb-comment-add-button:first-child {
931 937 display: none;
932 938 }
933 939 /* hide add comment button when only comment is being deleted */
934 940 .comment-deleting:first-child + .cb-comment-add-button {
935 941 display: none;
936 942 }
937 943 }
938 944 /**** END COMMENTS ****/
939 945
940 946 }
941 947
942 948
943 949 table.cb {
944 950 width: 100%;
945 951 border-collapse: collapse;
946 952
947 953 .cb-text {
948 954 padding: @cb-text-padding;
949 955 }
950 956 .cb-hunk {
951 957 padding: @cb-text-padding;
952 958 }
953 959 .cb-expand {
954 960 display: none;
955 961 }
956 962 .cb-collapse {
957 963 display: inline;
958 964 }
959 965 &.cb-collapsed {
960 966 .cb-line {
961 967 display: none;
962 968 }
963 969 .cb-expand {
964 970 display: inline;
965 971 }
966 972 .cb-collapse {
967 973 display: none;
968 974 }
969 975 }
970 976
971 977 /* intentionally general selector since .cb-line-selected must override it
972 978 and they both use !important since the td itself may have a random color
973 979 generated by annotation blocks. TLDR: if you change it, make sure
974 980 annotated block selection and line selection in file view still work */
975 981 .cb-line-fresh .cb-content {
976 982 background: white !important;
977 983 }
978 984 .cb-warning {
979 985 background: #fff4dd;
980 986 }
981 987
982 988 &.cb-diff-sideside {
983 989 td {
984 990 &.cb-content {
985 991 width: 50%;
986 992 }
987 993 }
988 994 }
989 995
990 996 tr {
991 997 &.cb-annotate {
992 998 border-top: 1px solid #eee;
993 999
994 1000 &+ .cb-line {
995 1001 border-top: 1px solid #eee;
996 1002 }
997 1003
998 1004 &:first-child {
999 1005 border-top: none;
1000 1006 &+ .cb-line {
1001 1007 border-top: none;
1002 1008 }
1003 1009 }
1004 1010 }
1005 1011
1006 1012 &.cb-hunk {
1007 1013 font-family: @font-family-monospace;
1008 1014 color: rgba(0, 0, 0, 0.3);
1009 1015
1010 1016 td {
1011 1017 &:first-child {
1012 1018 background: #edf2f9;
1013 1019 }
1014 1020 &:last-child {
1015 1021 background: #f4f7fb;
1016 1022 }
1017 1023 }
1018 1024 }
1019 1025 }
1020 1026
1021 1027 td {
1022 1028 vertical-align: top;
1023 1029 padding: 0;
1024 1030
1025 1031 &.cb-content {
1026 1032 font-size: 12.35px;
1027 1033
1028 1034 &.cb-line-selected .cb-code {
1029 1035 background: @comment-highlight-color !important;
1030 1036 }
1031 1037
1032 1038 span.cb-code {
1033 1039 line-height: @cb-line-height;
1034 1040 padding-left: @cb-line-code-padding;
1035 1041 padding-right: @cb-line-code-padding;
1036 1042 display: block;
1037 1043 white-space: pre-wrap;
1038 1044 font-family: @font-family-monospace;
1039 1045 word-break: break-word;
1046 .nonl {
1047 color: @color5;
1048 }
1040 1049 }
1041 1050
1042 1051 &> button.cb-comment-box-opener {
1043 1052 padding: 2px 6px 2px 6px;
1044 1053 margin-left: -20px;
1045 1054 margin-top: -2px;
1046 1055 border-radius: @border-radius;
1047 1056 position: absolute;
1048 1057 display: none;
1049 1058 }
1050 1059 .cb-comment {
1051 1060 margin-top: 10px;
1052 1061 white-space: normal;
1053 1062 }
1054 1063 }
1055 1064 &:hover {
1056 1065 button.cb-comment-box-opener {
1057 1066 display: block;
1058 1067 }
1059 1068 &+ td button.cb-comment-box-opener {
1060 1069 display: block
1061 1070 }
1062 1071 }
1063 1072
1073 &.cb-data {
1074 text-align: right;
1075 width: 30px;
1076 font-family: @font-family-monospace;
1077
1078 .icon-comment {
1079 cursor: pointer;
1080 }
1081 &.cb-line-selected > div {
1082 display: block;
1083 background: @comment-highlight-color !important;
1084 line-height: @cb-line-height;
1085 color: rgba(0, 0, 0, 0.3);
1086 }
1087 }
1088
1064 1089 &.cb-lineno {
1065 1090 padding: 0;
1066 1091 width: 50px;
1067 1092 color: rgba(0, 0, 0, 0.3);
1068 1093 text-align: right;
1069 1094 border-right: 1px solid #eee;
1070 1095 font-family: @font-family-monospace;
1071 1096
1072 1097 a::before {
1073 1098 content: attr(data-line-no);
1074 1099 }
1075 1100 &.cb-line-selected a {
1076 1101 background: @comment-highlight-color !important;
1077 1102 }
1078 1103
1079 1104 a {
1080 1105 display: block;
1081 1106 padding-right: @cb-line-code-padding;
1082 1107 padding-left: @cb-line-code-padding;
1083 1108 line-height: @cb-line-height;
1084 1109 color: rgba(0, 0, 0, 0.3);
1085 1110 }
1086 1111 }
1087 1112
1088 1113 &.cb-empty {
1089 1114 background: @grey7;
1090 1115 }
1091 1116
1092 1117 ins {
1093 1118 color: black;
1094 1119 background: #a6f3a6;
1095 1120 text-decoration: none;
1096 1121 }
1097 1122 del {
1098 1123 color: black;
1099 1124 background: #f8cbcb;
1100 1125 text-decoration: none;
1101 1126 }
1102 1127 &.cb-addition {
1103 1128 background: #ecffec;
1104 1129
1105 1130 &.blob-lineno {
1106 1131 background: #ddffdd;
1107 1132 }
1108 1133 }
1109 1134 &.cb-deletion {
1110 1135 background: #ffecec;
1111 1136
1112 1137 &.blob-lineno {
1113 1138 background: #ffdddd;
1114 1139 }
1115 1140 }
1116 1141
1117 1142 &.cb-annotate-info {
1118 1143 width: 320px;
1119 1144 min-width: 320px;
1120 1145 max-width: 320px;
1121 1146 padding: 5px 2px;
1122 1147 font-size: 13px;
1123 1148
1124 1149 strong.cb-annotate-message {
1125 1150 padding: 5px 0;
1126 1151 white-space: pre-line;
1127 1152 display: inline-block;
1128 1153 }
1129 1154 .rc-user {
1130 1155 float: none;
1131 1156 padding: 0 6px 0 17px;
1132 1157 min-width: auto;
1133 1158 min-height: auto;
1134 1159 }
1135 1160 }
1136 1161
1137 1162 &.cb-annotate-revision {
1138 1163 cursor: pointer;
1139 1164 text-align: right;
1140 1165 }
1141 1166 }
1142 1167 }
@@ -1,492 +1,494 b''
1 1 // # Copyright (C) 2010-2016 RhodeCode GmbH
2 2 // #
3 3 // # This program is free software: you can redistribute it and/or modify
4 4 // # it under the terms of the GNU Affero General Public License, version 3
5 5 // # (only), as published by the Free Software Foundation.
6 6 // #
7 7 // # This program is distributed in the hope that it will be useful,
8 8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 // # GNU General Public License for more details.
11 11 // #
12 12 // # You should have received a copy of the GNU Affero General Public License
13 13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 // #
15 15 // # This program is dual-licensed. If you wish to learn more about the
16 16 // # RhodeCode Enterprise Edition, including its added features, Support services,
17 17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
18 18
19 19 /**
20 20 RhodeCode JS Files
21 21 **/
22 22
23 23 if (typeof console == "undefined" || typeof console.log == "undefined"){
24 24 console = { log: function() {} }
25 25 }
26 26
27 27 // TODO: move the following function to submodules
28 28
29 29 /**
30 30 * show more
31 31 */
32 32 var show_more_event = function(){
33 33 $('table .show_more').click(function(e) {
34 34 var cid = e.target.id.substring(1);
35 35 var button = $(this);
36 36 if (button.hasClass('open')) {
37 37 $('#'+cid).hide();
38 38 button.removeClass('open');
39 39 } else {
40 40 $('#'+cid).show();
41 41 button.addClass('open one');
42 42 }
43 43 });
44 44 };
45 45
46 46 var compare_radio_buttons = function(repo_name, compare_ref_type){
47 47 $('#compare_action').on('click', function(e){
48 48 e.preventDefault();
49 49
50 50 var source = $('input[name=compare_source]:checked').val();
51 51 var target = $('input[name=compare_target]:checked').val();
52 52 if(source && target){
53 53 var url_data = {
54 54 repo_name: repo_name,
55 55 source_ref: source,
56 56 source_ref_type: compare_ref_type,
57 57 target_ref: target,
58 58 target_ref_type: compare_ref_type,
59 59 merge: 1
60 60 };
61 61 window.location = pyroutes.url('compare_url', url_data);
62 62 }
63 63 });
64 64 $('.compare-radio-button').on('click', function(e){
65 65 var source = $('input[name=compare_source]:checked').val();
66 66 var target = $('input[name=compare_target]:checked').val();
67 67 if(source && target){
68 68 $('#compare_action').removeAttr("disabled");
69 69 $('#compare_action').removeClass("disabled");
70 70 }
71 71 })
72 72 };
73 73
74 74 var showRepoSize = function(target, repo_name, commit_id, callback) {
75 75 var container = $('#' + target);
76 76 var url = pyroutes.url('repo_stats',
77 77 {"repo_name": repo_name, "commit_id": commit_id});
78 78
79 79 if (!container.hasClass('loaded')) {
80 80 $.ajax({url: url})
81 81 .complete(function (data) {
82 82 var responseJSON = data.responseJSON;
83 83 container.addClass('loaded');
84 84 container.html(responseJSON.size);
85 85 callback(responseJSON.code_stats)
86 86 })
87 87 .fail(function (data) {
88 88 console.log('failed to load repo stats');
89 89 });
90 90 }
91 91
92 92 };
93 93
94 94 var showRepoStats = function(target, data){
95 95 var container = $('#' + target);
96 96
97 97 if (container.hasClass('loaded')) {
98 98 return
99 99 }
100 100
101 101 var total = 0;
102 102 var no_data = true;
103 103 var tbl = document.createElement('table');
104 104 tbl.setAttribute('class', 'trending_language_tbl');
105 105
106 106 $.each(data, function(key, val){
107 107 total += val.count;
108 108 });
109 109
110 110 var sortedStats = [];
111 111 for (var obj in data){
112 112 sortedStats.push([obj, data[obj]])
113 113 }
114 114 var sortedData = sortedStats.sort(function (a, b) {
115 115 return b[1].count - a[1].count
116 116 });
117 117 var cnt = 0;
118 118 $.each(sortedData, function(idx, val){
119 119 cnt += 1;
120 120 no_data = false;
121 121
122 122 var hide = cnt > 2;
123 123 var tr = document.createElement('tr');
124 124 if (hide) {
125 125 tr.setAttribute('style', 'display:none');
126 126 tr.setAttribute('class', 'stats_hidden');
127 127 }
128 128
129 129 var key = val[0];
130 130 var obj = {"desc": val[1].desc, "count": val[1].count};
131 131
132 132 var percentage = Math.round((obj.count / total * 100), 2);
133 133
134 134 var td1 = document.createElement('td');
135 135 td1.width = 300;
136 136 var trending_language_label = document.createElement('div');
137 137 trending_language_label.innerHTML = obj.desc + " (.{0})".format(key);
138 138 td1.appendChild(trending_language_label);
139 139
140 140 var td2 = document.createElement('td');
141 141 var trending_language = document.createElement('div');
142 142 var nr_files = obj.count +" "+ _ngettext('file', 'files', obj.count);
143 143
144 144 trending_language.title = key + " " + nr_files;
145 145
146 146 trending_language.innerHTML = "<span>" + percentage + "% " + nr_files
147 147 + "</span><b>" + percentage + "% " + nr_files + "</b>";
148 148
149 149 trending_language.setAttribute("class", 'trending_language');
150 150 $('b', trending_language)[0].style.width = percentage + "%";
151 151 td2.appendChild(trending_language);
152 152
153 153 tr.appendChild(td1);
154 154 tr.appendChild(td2);
155 155 tbl.appendChild(tr);
156 156 if (cnt == 3) {
157 157 var show_more = document.createElement('tr');
158 158 var td = document.createElement('td');
159 159 lnk = document.createElement('a');
160 160
161 161 lnk.href = '#';
162 162 lnk.innerHTML = _gettext('Show more');
163 163 lnk.id = 'code_stats_show_more';
164 164 td.appendChild(lnk);
165 165
166 166 show_more.appendChild(td);
167 167 show_more.appendChild(document.createElement('td'));
168 168 tbl.appendChild(show_more);
169 169 }
170 170 });
171 171
172 172 $(container).html(tbl);
173 173 $(container).addClass('loaded');
174 174
175 175 $('#code_stats_show_more').on('click', function (e) {
176 176 e.preventDefault();
177 177 $('.stats_hidden').each(function (idx) {
178 178 $(this).css("display", "");
179 179 });
180 180 $('#code_stats_show_more').hide();
181 181 });
182 182
183 183 };
184 184
185 185
186 186 // Toggle Collapsable Content
187 187 function collapsableContent() {
188 188
189 189 $('.collapsable-content').not('.no-hide').hide();
190 190
191 191 $('.btn-collapse').unbind(); //in case we've been here before
192 192 $('.btn-collapse').click(function() {
193 193 var button = $(this);
194 194 var togglename = $(this).data("toggle");
195 195 $('.collapsable-content[data-toggle='+togglename+']').toggle();
196 196 if ($(this).html()=="Show Less")
197 197 $(this).html("Show More");
198 198 else
199 199 $(this).html("Show Less");
200 200 });
201 201 };
202 202
203 203 var timeagoActivate = function() {
204 204 $("time.timeago").timeago();
205 205 };
206 206
207 207 // Formatting values in a Select2 dropdown of commit references
208 208 var formatSelect2SelectionRefs = function(commit_ref){
209 209 var tmpl = '';
210 210 if (!commit_ref.text || commit_ref.type === 'sha'){
211 211 return commit_ref.text;
212 212 }
213 213 if (commit_ref.type === 'branch'){
214 214 tmpl = tmpl.concat('<i class="icon-branch"></i> ');
215 215 } else if (commit_ref.type === 'tag'){
216 216 tmpl = tmpl.concat('<i class="icon-tag"></i> ');
217 217 } else if (commit_ref.type === 'book'){
218 218 tmpl = tmpl.concat('<i class="icon-bookmark"></i> ');
219 219 }
220 220 return tmpl.concat(commit_ref.text);
221 221 };
222 222
223 223 // takes a given html element and scrolls it down offset pixels
224 224 function offsetScroll(element, offset) {
225 225 setTimeout(function() {
226 226 var location = element.offset().top;
227 227 // some browsers use body, some use html
228 228 $('html, body').animate({ scrollTop: (location - offset) });
229 229 }, 100);
230 230 }
231 231
232 232 // scroll an element `percent`% from the top of page in `time` ms
233 233 function scrollToElement(element, percent, time) {
234 234 percent = (percent === undefined ? 25 : percent);
235 235 time = (time === undefined ? 100 : time);
236 236
237 237 var $element = $(element);
238 238 var elOffset = $element.offset().top;
239 239 var elHeight = $element.height();
240 240 var windowHeight = $(window).height();
241 241 var offset = elOffset;
242 242 if (elHeight < windowHeight) {
243 243 offset = elOffset - ((windowHeight / (100 / percent)) - (elHeight / 2));
244 244 }
245 245 setTimeout(function() {
246 246 $('html, body').animate({ scrollTop: offset});
247 247 }, time);
248 248 }
249 249
250 250 /**
251 251 * global hooks after DOM is loaded
252 252 */
253 253 $(document).ready(function() {
254 254 firefoxAnchorFix();
255 255
256 256 $('.navigation a.menulink').on('click', function(e){
257 257 var menuitem = $(this).parent('li');
258 258 if (menuitem.hasClass('open')) {
259 259 menuitem.removeClass('open');
260 260 } else {
261 261 menuitem.addClass('open');
262 262 $(document).on('click', function(event) {
263 263 if (!$(event.target).closest(menuitem).length) {
264 264 menuitem.removeClass('open');
265 265 }
266 266 });
267 267 }
268 268 });
269 269 $('.compare_view_files').on(
270 270 'mouseenter mouseleave', 'tr.line .lineno a',function(event) {
271 271 if (event.type === "mouseenter") {
272 272 $(this).parents('tr.line').addClass('hover');
273 273 } else {
274 274 $(this).parents('tr.line').removeClass('hover');
275 275 }
276 276 });
277 277
278 278 $('.compare_view_files').on(
279 279 'mouseenter mouseleave', 'tr.line .add-comment-line a',function(event){
280 280 if (event.type === "mouseenter") {
281 281 $(this).parents('tr.line').addClass('commenting');
282 282 } else {
283 283 $(this).parents('tr.line').removeClass('commenting');
284 284 }
285 285 });
286 286
287 287 $('body').on( /* TODO: replace the $('.compare_view_files').on('click') below
288 288 when new diffs are integrated */
289 289 'click', '.cb-lineno a', function(event) {
290 290
291 291 if ($(this).attr('data-line-no') !== ""){
292 292 $('.cb-line-selected').removeClass('cb-line-selected');
293 293 var td = $(this).parent();
294 294 td.addClass('cb-line-selected'); // line number td
295 td.prev().addClass('cb-line-selected'); // line data td
295 296 td.next().addClass('cb-line-selected'); // line content td
296 297
297 298 // Replace URL without jumping to it if browser supports.
298 299 // Default otherwise
299 300 if (history.pushState) {
300 301 var new_location = location.href.rstrip('#');
301 302 if (location.hash) {
302 303 new_location = new_location.replace(location.hash, "");
303 304 }
304 305
305 306 // Make new anchor url
306 307 new_location = new_location + $(this).attr('href');
307 308 history.pushState(true, document.title, new_location);
308 309
309 310 return false;
310 311 }
311 312 }
312 313 });
313 314
314 315 $('.compare_view_files').on( /* TODO: replace this with .cb function above
315 316 when new diffs are integrated */
316 317 'click', 'tr.line .lineno a',function(event) {
317 318 if ($(this).text() != ""){
318 319 $('tr.line').removeClass('selected');
319 320 $(this).parents("tr.line").addClass('selected');
320 321
321 322 // Replace URL without jumping to it if browser supports.
322 323 // Default otherwise
323 324 if (history.pushState) {
324 325 var new_location = location.href;
325 326 if (location.hash){
326 327 new_location = new_location.replace(location.hash, "");
327 328 }
328 329
329 330 // Make new anchor url
330 331 var new_location = new_location+$(this).attr('href');
331 332 history.pushState(true, document.title, new_location);
332 333
333 334 return false;
334 335 }
335 336 }
336 337 });
337 338
338 339 $('.compare_view_files').on(
339 340 'click', 'tr.line .add-comment-line a',function(event) {
340 341 var tr = $(event.currentTarget).parents('tr.line')[0];
341 342 injectInlineForm(tr);
342 343 return false;
343 344 });
344 345
345 346 $('.collapse_file').on('click', function(e) {
346 347 e.stopPropagation();
347 348 if ($(e.target).is('a')) { return; }
348 349 var node = $(e.delegateTarget).first();
349 350 var icon = $($(node.children().first()).children().first());
350 351 var id = node.attr('fid');
351 352 var target = $('#'+id);
352 353 var tr = $('#tr_'+id);
353 354 var diff = $('#diff_'+id);
354 355 if(node.hasClass('expand_file')){
355 356 node.removeClass('expand_file');
356 357 icon.removeClass('expand_file_icon');
357 358 node.addClass('collapse_file');
358 359 icon.addClass('collapse_file_icon');
359 360 diff.show();
360 361 tr.show();
361 362 target.show();
362 363 } else {
363 364 node.removeClass('collapse_file');
364 365 icon.removeClass('collapse_file_icon');
365 366 node.addClass('expand_file');
366 367 icon.addClass('expand_file_icon');
367 368 diff.hide();
368 369 tr.hide();
369 370 target.hide();
370 371 }
371 372 });
372 373
373 374 $('#expand_all_files').click(function() {
374 375 $('.expand_file').each(function() {
375 376 var node = $(this);
376 377 var icon = $($(node.children().first()).children().first());
377 378 var id = $(this).attr('fid');
378 379 var target = $('#'+id);
379 380 var tr = $('#tr_'+id);
380 381 var diff = $('#diff_'+id);
381 382 node.removeClass('expand_file');
382 383 icon.removeClass('expand_file_icon');
383 384 node.addClass('collapse_file');
384 385 icon.addClass('collapse_file_icon');
385 386 diff.show();
386 387 tr.show();
387 388 target.show();
388 389 });
389 390 });
390 391
391 392 $('#collapse_all_files').click(function() {
392 393 $('.collapse_file').each(function() {
393 394 var node = $(this);
394 395 var icon = $($(node.children().first()).children().first());
395 396 var id = $(this).attr('fid');
396 397 var target = $('#'+id);
397 398 var tr = $('#tr_'+id);
398 399 var diff = $('#diff_'+id);
399 400 node.removeClass('collapse_file');
400 401 icon.removeClass('collapse_file_icon');
401 402 node.addClass('expand_file');
402 403 icon.addClass('expand_file_icon');
403 404 diff.hide();
404 405 tr.hide();
405 406 target.hide();
406 407 });
407 408 });
408 409
409 410 // Mouse over behavior for comments and line selection
410 411
411 412 // Select the line that comes from the url anchor
412 413 // At the time of development, Chrome didn't seem to support jquery's :target
413 414 // element, so I had to scroll manually
414 415
415 416 if (location.hash) { /* TODO: dan: remove this and replace with code block
416 417 below when new diffs are ready */
417 418 var result = splitDelimitedHash(location.hash);
418 419 var loc = result.loc;
419 420 if (loc.length > 1){
420 421 var lineno = $(loc+'.lineno');
421 422 if (lineno.length > 0){
422 423 var tr = lineno.parents('tr.line');
423 424 tr.addClass('selected');
424 425
425 426 tr[0].scrollIntoView();
426 427
427 428 $.Topic('/ui/plugins/code/anchor_focus').prepareOrPublish({
428 429 tr: tr,
429 430 remainder: result.remainder});
430 431 }
431 432 }
432 433 }
433 434
434 435 if (location.hash) { /* TODO: dan: use this to replace the code block above
435 436 when new diffs are ready */
436 437 var result = splitDelimitedHash(location.hash);
437 438 var loc = result.loc;
438 439 if (loc.length > 1) {
439 440
440 441 var highlightable_line_tds = [];
441 442
442 443 // source code line format
443 444 var page_highlights = loc.substring(
444 445 loc.indexOf('#') + 1).split('L');
445 446
446 447 if (page_highlights.length > 1) {
447 448 var highlight_ranges = page_highlights[1].split(",");
448 449 var h_lines = [];
449 450 for (var pos in highlight_ranges) {
450 451 var _range = highlight_ranges[pos].split('-');
451 452 if (_range.length === 2) {
452 453 var start = parseInt(_range[0]);
453 454 var end = parseInt(_range[1]);
454 455 if (start < end) {
455 456 for (var i = start; i <= end; i++) {
456 457 h_lines.push(i);
457 458 }
458 459 }
459 460 }
460 461 else {
461 462 h_lines.push(parseInt(highlight_ranges[pos]));
462 463 }
463 464 }
464 465 for (pos in h_lines) {
465 466 var line_td = $('td.cb-lineno#L' + h_lines[pos]);
466 467 if (line_td.length) {
467 468 highlightable_line_tds.push(line_td);
468 469 }
469 470 }
470 471 }
471 472
472 473 // now check a direct id reference (diff page)
473 474 if ($(loc).length && $(loc).hasClass('cb-lineno')) {
474 475 highlightable_line_tds.push($(loc));
475 476 }
476 477 $.each(highlightable_line_tds, function (i, $td) {
477 478 $td.addClass('cb-line-selected'); // line number td
479 $td.prev().addClass('cb-line-selected'); // line data
478 480 $td.next().addClass('cb-line-selected'); // line content
479 481 });
480 482
481 483 if (highlightable_line_tds.length) {
482 484 var $first_line_td = highlightable_line_tds[0];
483 485 scrollToElement($first_line_td);
484 486 $.Topic('/ui/plugins/code/anchor_focus').prepareOrPublish({
485 487 lineno: $first_line_td,
486 488 remainder: result.remainder
487 489 });
488 490 }
489 491 }
490 492 }
491 493 collapsableContent();
492 494 });
@@ -1,672 +1,897 b''
1 1 // # Copyright (C) 2010-2016 RhodeCode GmbH
2 2 // #
3 3 // # This program is free software: you can redistribute it and/or modify
4 4 // # it under the terms of the GNU Affero General Public License, version 3
5 5 // # (only), as published by the Free Software Foundation.
6 6 // #
7 7 // # This program is distributed in the hope that it will be useful,
8 8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 // # GNU General Public License for more details.
11 11 // #
12 12 // # You should have received a copy of the GNU Affero General Public License
13 13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 // #
15 15 // # This program is dual-licensed. If you wish to learn more about the
16 16 // # RhodeCode Enterprise Edition, including its added features, Support services,
17 17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
18 18
19 19 var firefoxAnchorFix = function() {
20 20 // hack to make anchor links behave properly on firefox, in our inline
21 21 // comments generation when comments are injected firefox is misbehaving
22 22 // when jumping to anchor links
23 23 if (location.href.indexOf('#') > -1) {
24 24 location.href += '';
25 25 }
26 26 };
27 27
28 28 // returns a node from given html;
29 29 var fromHTML = function(html){
30 30 var _html = document.createElement('element');
31 31 _html.innerHTML = html;
32 32 return _html;
33 33 };
34 34
35 35 var tableTr = function(cls, body){
36 36 var _el = document.createElement('div');
37 37 var _body = $(body).attr('id');
38 38 var comment_id = fromHTML(body).children[0].id.split('comment-')[1];
39 39 var id = 'comment-tr-{0}'.format(comment_id);
40 40 var _html = ('<table><tbody><tr id="{0}" class="{1}">'+
41 41 '<td class="add-comment-line tooltip tooltip" title="Add Comment"><span class="add-comment-content"></span></td>'+
42 42 '<td></td>'+
43 43 '<td></td>'+
44 44 '<td></td>'+
45 45 '<td>{2}</td>'+
46 46 '</tr></tbody></table>').format(id, cls, body);
47 47 $(_el).html(_html);
48 48 return _el.children[0].children[0].children[0];
49 49 };
50 50
51 51 var removeInlineForm = function(form) {
52 52 form.parentNode.removeChild(form);
53 53 };
54 54
55 55 var createInlineForm = function(parent_tr, f_path, line) {
56 56 var tmpl = $('#comment-inline-form-template').html();
57 57 tmpl = tmpl.format(f_path, line);
58 58 var form = tableTr('comment-form-inline', tmpl);
59 59 var form_hide_button = $(form).find('.hide-inline-form');
60 60
61 61 $(form_hide_button).click(function(e) {
62 62 $('.inline-comments').removeClass('hide-comment-button');
63 63 var newtr = e.currentTarget.parentNode.parentNode.parentNode.parentNode.parentNode;
64 64 if ($(newtr.nextElementSibling).hasClass('inline-comments-button')) {
65 65 $(newtr.nextElementSibling).show();
66 66 }
67 67 $(newtr).parents('.comment-form-inline').remove();
68 68 $(parent_tr).removeClass('form-open');
69 69 $(parent_tr).removeClass('hl-comment');
70 70 });
71 71
72 72 return form;
73 73 };
74 74
75 75 var getLineNo = function(tr) {
76 76 var line;
77 77 // Try to get the id and return "" (empty string) if it doesn't exist
78 78 var o = ($(tr).find('.lineno.old').attr('id')||"").split('_');
79 79 var n = ($(tr).find('.lineno.new').attr('id')||"").split('_');
80 80 if (n.length >= 2) {
81 81 line = n[n.length-1];
82 82 } else if (o.length >= 2) {
83 83 line = o[o.length-1];
84 84 }
85 85 return line;
86 86 };
87 87
88 88 /**
89 89 * make a single inline comment and place it inside
90 90 */
91 91 var renderInlineComment = function(json_data, show_add_button) {
92 92 show_add_button = typeof show_add_button !== 'undefined' ? show_add_button : true;
93 93 try {
94 94 var html = json_data.rendered_text;
95 95 var lineno = json_data.line_no;
96 96 var target_id = json_data.target_id;
97 97 placeInline(target_id, lineno, html, show_add_button);
98 98 } catch (e) {
99 99 console.error(e);
100 100 }
101 101 };
102 102
103 103 function bindDeleteCommentButtons() {
104 104 $('.delete-comment').one('click', function() {
105 105 var comment_id = $(this).data("comment-id");
106 106
107 107 if (comment_id){
108 108 deleteComment(comment_id);
109 109 }
110 110 });
111 111 }
112 112
113 113 /**
114 114 * Inject inline comment for on given TR this tr should be always an .line
115 115 * tr containing the line. Code will detect comment, and always put the comment
116 116 * block at the very bottom
117 117 */
118 118 var injectInlineForm = function(tr){
119 119 if (!$(tr).hasClass('line')) {
120 120 return;
121 121 }
122 122
123 123 var _td = $(tr).find('.code').get(0);
124 124 if ($(tr).hasClass('form-open') ||
125 125 $(tr).hasClass('context') ||
126 126 $(_td).hasClass('no-comment')) {
127 127 return;
128 128 }
129 129 $(tr).addClass('form-open');
130 130 $(tr).addClass('hl-comment');
131 131 var node = $(tr.parentNode.parentNode.parentNode).find('.full_f_path').get(0);
132 132 var f_path = $(node).attr('path');
133 133 var lineno = getLineNo(tr);
134 134 var form = createInlineForm(tr, f_path, lineno);
135 135
136 136 var parent = tr;
137 137 while (1) {
138 138 var n = parent.nextElementSibling;
139 139 // next element are comments !
140 140 if ($(n).hasClass('inline-comments')) {
141 141 parent = n;
142 142 }
143 143 else {
144 144 break;
145 145 }
146 146 }
147 147 var _parent = $(parent).get(0);
148 148 $(_parent).after(form);
149 149 $('.comment-form-inline').prev('.inline-comments').addClass('hide-comment-button');
150 150 var f = $(form).get(0);
151 151
152 152 var _form = $(f).find('.inline-form').get(0);
153 153
154 154 var pullRequestId = templateContext.pull_request_data.pull_request_id;
155 155 var commitId = templateContext.commit_data.commit_id;
156 156
157 157 var commentForm = new CommentForm(_form, commitId, pullRequestId, lineno, false);
158 158 var cm = commentForm.getCmInstance();
159 159
160 160 // set a CUSTOM submit handler for inline comments.
161 161 commentForm.setHandleFormSubmit(function(o) {
162 162 var text = commentForm.cm.getValue();
163 163
164 164 if (text === "") {
165 165 return;
166 166 }
167 167
168 168 if (lineno === undefined) {
169 169 alert('missing line !');
170 170 return;
171 171 }
172 172 if (f_path === undefined) {
173 173 alert('missing file path !');
174 174 return;
175 175 }
176 176
177 177 var excludeCancelBtn = false;
178 178 var submitEvent = true;
179 179 commentForm.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
180 180 commentForm.cm.setOption("readOnly", true);
181 181 var postData = {
182 182 'text': text,
183 183 'f_path': f_path,
184 184 'line': lineno,
185 185 'csrf_token': CSRF_TOKEN
186 186 };
187 187 var submitSuccessCallback = function(o) {
188 188 $(tr).removeClass('form-open');
189 189 removeInlineForm(f);
190 190 renderInlineComment(o);
191 191 $('.inline-comments').removeClass('hide-comment-button');
192 192
193 193 // re trigger the linkification of next/prev navigation
194 194 linkifyComments($('.inline-comment-injected'));
195 195 timeagoActivate();
196 196 bindDeleteCommentButtons();
197 197 commentForm.setActionButtonsDisabled(false);
198 198
199 199 };
200 200 var submitFailCallback = function(){
201 201 commentForm.resetCommentFormState(text)
202 202 };
203 203 commentForm.submitAjaxPOST(
204 204 commentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback);
205 205 });
206 206
207 207 setTimeout(function() {
208 208 // callbacks
209 209 if (cm !== undefined) {
210 210 cm.focus();
211 211 }
212 212 }, 10);
213 213
214 214 $.Topic('/ui/plugins/code/comment_form_built').prepareOrPublish({
215 215 form:_form,
216 216 parent:_parent,
217 217 lineno: lineno,
218 218 f_path: f_path}
219 219 );
220 220 };
221 221
222 222 var deleteComment = function(comment_id) {
223 223 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id);
224 224 var postData = {
225 225 '_method': 'delete',
226 226 'csrf_token': CSRF_TOKEN
227 227 };
228 228
229 229 var success = function(o) {
230 230 window.location.reload();
231 231 };
232 232 ajaxPOST(url, postData, success);
233 233 };
234 234
235 235 var createInlineAddButton = function(tr){
236 236 var label = _gettext('Add another comment');
237 237 var html_el = document.createElement('div');
238 238 $(html_el).addClass('add-comment');
239 239 html_el.innerHTML = '<span class="btn btn-secondary">{0}</span>'.format(label);
240 240 var add = new $(html_el);
241 241 add.on('click', function(e) {
242 242 injectInlineForm(tr);
243 243 });
244 244 return add;
245 245 };
246 246
247 247 var placeAddButton = function(target_tr){
248 248 if(!target_tr){
249 249 return;
250 250 }
251 251 var last_node = target_tr;
252 252 // scan
253 253 while (1){
254 254 var n = last_node.nextElementSibling;
255 255 // next element are comments !
256 256 if($(n).hasClass('inline-comments')){
257 257 last_node = n;
258 258 // also remove the comment button from previous
259 259 var comment_add_buttons = $(last_node).find('.add-comment');
260 260 for(var i=0; i<comment_add_buttons.length; i++){
261 261 var b = comment_add_buttons[i];
262 262 b.parentNode.removeChild(b);
263 263 }
264 264 }
265 265 else{
266 266 break;
267 267 }
268 268 }
269 269 var add = createInlineAddButton(target_tr);
270 270 // get the comment div
271 271 var comment_block = $(last_node).find('.comment')[0];
272 272 // attach add button
273 273 $(add).insertAfter(comment_block);
274 274 };
275 275
276 276 /**
277 277 * Places the inline comment into the changeset block in proper line position
278 278 */
279 279 var placeInline = function(target_container, lineno, html, show_add_button) {
280 280 show_add_button = typeof show_add_button !== 'undefined' ? show_add_button : true;
281 281
282 282 var lineid = "{0}_{1}".format(target_container, lineno);
283 283 var target_line = $('#' + lineid).get(0);
284 284 var comment = new $(tableTr('inline-comments', html));
285 285 // check if there are comments already !
286 286 if (target_line) {
287 287 var parent_node = target_line.parentNode;
288 288 var root_parent = parent_node;
289 289
290 290 while (1) {
291 291 var n = parent_node.nextElementSibling;
292 292 // next element are comments !
293 293 if ($(n).hasClass('inline-comments')) {
294 294 parent_node = n;
295 295 }
296 296 else {
297 297 break;
298 298 }
299 299 }
300 300 // put in the comment at the bottom
301 301 $(comment).insertAfter(parent_node);
302 302 $(comment).find('.comment-inline').addClass('inline-comment-injected');
303 303 // scan nodes, and attach add button to last one
304 304 if (show_add_button) {
305 305 placeAddButton(root_parent);
306 306 }
307 307 addCommentToggle(target_line);
308 308 }
309 309
310 310 return target_line;
311 311 };
312 312
313 313 var addCommentToggle = function(target_line) {
314 314 // exposes comment toggle button
315 315 $(target_line).siblings('.comment-toggle').addClass('active');
316 316 return;
317 317 };
318 318
319 319 var bindToggleButtons = function() {
320 320 $('.comment-toggle').on('click', function() {
321 321 $(this).parent().nextUntil('tr.line').toggle('inline-comments');
322 322 });
323 323 };
324 324
325 325 var linkifyComments = function(comments) {
326 326 /* TODO: dan: remove this - it should no longer needed */
327 327 for (var i = 0; i < comments.length; i++) {
328 328 var comment_id = $(comments[i]).data('comment-id');
329 329 var prev_comment_id = $(comments[i - 1]).data('comment-id');
330 330 var next_comment_id = $(comments[i + 1]).data('comment-id');
331 331
332 332 // place next/prev links
333 333 if (prev_comment_id) {
334 334 $('#prev_c_' + comment_id).show();
335 335 $('#prev_c_' + comment_id + " a.arrow_comment_link").attr(
336 336 'href', '#comment-' + prev_comment_id).removeClass('disabled');
337 337 }
338 338 if (next_comment_id) {
339 339 $('#next_c_' + comment_id).show();
340 340 $('#next_c_' + comment_id + " a.arrow_comment_link").attr(
341 341 'href', '#comment-' + next_comment_id).removeClass('disabled');
342 342 }
343 343 // place a first link to the total counter
344 344 if (i === 0) {
345 345 $('#inline-comments-counter').attr('href', '#comment-' + comment_id);
346 346 }
347 347 }
348 348
349 349 };
350 350
351 351 /**
352 352 * Iterates over all the inlines, and places them inside proper blocks of data
353 353 */
354 354 var renderInlineComments = function(file_comments, show_add_button) {
355 355 show_add_button = typeof show_add_button !== 'undefined' ? show_add_button : true;
356 356
357 357 for (var i = 0; i < file_comments.length; i++) {
358 358 var box = file_comments[i];
359 359
360 360 var target_id = $(box).attr('target_id');
361 361
362 362 // actually comments with line numbers
363 363 var comments = box.children;
364 364
365 365 for (var j = 0; j < comments.length; j++) {
366 366 var data = {
367 367 'rendered_text': comments[j].outerHTML,
368 368 'line_no': $(comments[j]).attr('line'),
369 369 'target_id': target_id
370 370 };
371 371 renderInlineComment(data, show_add_button);
372 372 }
373 373 }
374 374
375 375 // since order of injection is random, we're now re-iterating
376 376 // from correct order and filling in links
377 377 linkifyComments($('.inline-comment-injected'));
378 378 bindDeleteCommentButtons();
379 379 firefoxAnchorFix();
380 380 };
381 381
382 382
383 383 /* Comment form for main and inline comments */
384 384 var CommentForm = (function() {
385 385 "use strict";
386 386
387 387 function CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions) {
388 388
389 389 this.withLineNo = function(selector) {
390 390 var lineNo = this.lineNo;
391 391 if (lineNo === undefined) {
392 392 return selector
393 393 } else {
394 394 return selector + '_' + lineNo;
395 395 }
396 396 };
397 397
398 398 this.commitId = commitId;
399 399 this.pullRequestId = pullRequestId;
400 400 this.lineNo = lineNo;
401 401 this.initAutocompleteActions = initAutocompleteActions;
402 402
403 403 this.previewButton = this.withLineNo('#preview-btn');
404 404 this.previewContainer = this.withLineNo('#preview-container');
405 405
406 406 this.previewBoxSelector = this.withLineNo('#preview-box');
407 407
408 408 this.editButton = this.withLineNo('#edit-btn');
409 409 this.editContainer = this.withLineNo('#edit-container');
410 410
411 411 this.cancelButton = this.withLineNo('#cancel-btn');
412 412
413 413 this.statusChange = '#change_status';
414 414 this.cmBox = this.withLineNo('#text');
415 415 this.cm = initCommentBoxCodeMirror(this.cmBox, this.initAutocompleteActions);
416 416
417 417 this.submitForm = formElement;
418 418 this.submitButton = $(this.submitForm).find('input[type="submit"]');
419 419 this.submitButtonText = this.submitButton.val();
420 420
421 421 this.previewUrl = pyroutes.url('changeset_comment_preview',
422 422 {'repo_name': templateContext.repo_name});
423 423
424 424 // based on commitId, or pullReuqestId decide where do we submit
425 425 // out data
426 426 if (this.commitId){
427 427 this.submitUrl = pyroutes.url('changeset_comment',
428 428 {'repo_name': templateContext.repo_name,
429 429 'revision': this.commitId});
430 430
431 431 } else if (this.pullRequestId) {
432 432 this.submitUrl = pyroutes.url('pullrequest_comment',
433 433 {'repo_name': templateContext.repo_name,
434 434 'pull_request_id': this.pullRequestId});
435 435
436 436 } else {
437 437 throw new Error(
438 438 'CommentForm requires pullRequestId, or commitId to be specified.')
439 439 }
440 440
441 441 this.getCmInstance = function(){
442 442 return this.cm
443 443 };
444 444
445 445 var self = this;
446 446
447 447 this.getCommentStatus = function() {
448 448 return $(this.submitForm).find(this.statusChange).val();
449 449 };
450 450
451 451 this.isAllowedToSubmit = function() {
452 452 return !$(this.submitButton).prop('disabled');
453 453 };
454 454
455 455 this.initStatusChangeSelector = function(){
456 456 var formatChangeStatus = function(state, escapeMarkup) {
457 457 var originalOption = state.element;
458 458 return '<div class="flag_status ' + $(originalOption).data('status') + ' pull-left"></div>' +
459 459 '<span>' + escapeMarkup(state.text) + '</span>';
460 460 };
461 461 var formatResult = function(result, container, query, escapeMarkup) {
462 462 return formatChangeStatus(result, escapeMarkup);
463 463 };
464 464
465 465 var formatSelection = function(data, container, escapeMarkup) {
466 466 return formatChangeStatus(data, escapeMarkup);
467 467 };
468 468
469 469 $(this.submitForm).find(this.statusChange).select2({
470 470 placeholder: _gettext('Status Review'),
471 471 formatResult: formatResult,
472 472 formatSelection: formatSelection,
473 473 containerCssClass: "drop-menu status_box_menu",
474 474 dropdownCssClass: "drop-menu-dropdown",
475 475 dropdownAutoWidth: true,
476 476 minimumResultsForSearch: -1
477 477 });
478 478 $(this.submitForm).find(this.statusChange).on('change', function() {
479 479 var status = self.getCommentStatus();
480 480 if (status && !self.lineNo) {
481 481 $(self.submitButton).prop('disabled', false);
482 482 }
483 483 //todo, fix this name
484 484 var placeholderText = _gettext('Comment text will be set automatically based on currently selected status ({0}) ...').format(status);
485 485 self.cm.setOption('placeholder', placeholderText);
486 486 })
487 487 };
488 488
489 489 // reset the comment form into it's original state
490 490 this.resetCommentFormState = function(content) {
491 491 content = content || '';
492 492
493 493 $(this.editContainer).show();
494 494 $(this.editButton).hide();
495 495
496 496 $(this.previewContainer).hide();
497 497 $(this.previewButton).show();
498 498
499 499 this.setActionButtonsDisabled(true);
500 500 self.cm.setValue(content);
501 501 self.cm.setOption("readOnly", false);
502 502 };
503 503
504 504 this.submitAjaxPOST = function(url, postData, successHandler, failHandler) {
505 505 failHandler = failHandler || function() {};
506 506 var postData = toQueryString(postData);
507 507 var request = $.ajax({
508 508 url: url,
509 509 type: 'POST',
510 510 data: postData,
511 511 headers: {'X-PARTIAL-XHR': true}
512 512 })
513 513 .done(function(data) {
514 514 successHandler(data);
515 515 })
516 516 .fail(function(data, textStatus, errorThrown){
517 517 alert(
518 518 "Error while submitting comment.\n" +
519 519 "Error code {0} ({1}).".format(data.status, data.statusText));
520 520 failHandler()
521 521 });
522 522 return request;
523 523 };
524 524
525 525 // overwrite a submitHandler, we need to do it for inline comments
526 526 this.setHandleFormSubmit = function(callback) {
527 527 this.handleFormSubmit = callback;
528 528 };
529 529
530 530 // default handler for for submit for main comments
531 531 this.handleFormSubmit = function() {
532 532 var text = self.cm.getValue();
533 533 var status = self.getCommentStatus();
534 534
535 535 if (text === "" && !status) {
536 536 return;
537 537 }
538 538
539 539 var excludeCancelBtn = false;
540 540 var submitEvent = true;
541 541 self.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
542 542 self.cm.setOption("readOnly", true);
543 543 var postData = {
544 544 'text': text,
545 545 'changeset_status': status,
546 546 'csrf_token': CSRF_TOKEN
547 547 };
548 548
549 549 var submitSuccessCallback = function(o) {
550 550 if (status) {
551 551 location.reload(true);
552 552 } else {
553 553 $('#injected_page_comments').append(o.rendered_text);
554 554 self.resetCommentFormState();
555 555 bindDeleteCommentButtons();
556 556 timeagoActivate();
557 557 }
558 558 };
559 559 var submitFailCallback = function(){
560 560 self.resetCommentFormState(text)
561 561 };
562 562 self.submitAjaxPOST(
563 563 self.submitUrl, postData, submitSuccessCallback, submitFailCallback);
564 564 };
565 565
566 566 this.previewSuccessCallback = function(o) {
567 567 $(self.previewBoxSelector).html(o);
568 568 $(self.previewBoxSelector).removeClass('unloaded');
569 569
570 570 // swap buttons
571 571 $(self.previewButton).hide();
572 572 $(self.editButton).show();
573 573
574 574 // unlock buttons
575 575 self.setActionButtonsDisabled(false);
576 576 };
577 577
578 578 this.setActionButtonsDisabled = function(state, excludeCancelBtn, submitEvent) {
579 579 excludeCancelBtn = excludeCancelBtn || false;
580 580 submitEvent = submitEvent || false;
581 581
582 582 $(this.editButton).prop('disabled', state);
583 583 $(this.previewButton).prop('disabled', state);
584 584
585 585 if (!excludeCancelBtn) {
586 586 $(this.cancelButton).prop('disabled', state);
587 587 }
588 588
589 589 var submitState = state;
590 590 if (!submitEvent && this.getCommentStatus() && !this.lineNo) {
591 591 // if the value of commit review status is set, we allow
592 592 // submit button, but only on Main form, lineNo means inline
593 593 submitState = false
594 594 }
595 595 $(this.submitButton).prop('disabled', submitState);
596 596 if (submitEvent) {
597 597 $(this.submitButton).val(_gettext('Submitting...'));
598 598 } else {
599 599 $(this.submitButton).val(this.submitButtonText);
600 600 }
601 601
602 602 };
603 603
604 604 // lock preview/edit/submit buttons on load, but exclude cancel button
605 605 var excludeCancelBtn = true;
606 606 this.setActionButtonsDisabled(true, excludeCancelBtn);
607 607
608 608 // anonymous users don't have access to initialized CM instance
609 609 if (this.cm !== undefined){
610 610 this.cm.on('change', function(cMirror) {
611 611 if (cMirror.getValue() === "") {
612 612 self.setActionButtonsDisabled(true, excludeCancelBtn)
613 613 } else {
614 614 self.setActionButtonsDisabled(false, excludeCancelBtn)
615 615 }
616 616 });
617 617 }
618 618
619 619 $(this.editButton).on('click', function(e) {
620 620 e.preventDefault();
621 621
622 622 $(self.previewButton).show();
623 623 $(self.previewContainer).hide();
624 624 $(self.editButton).hide();
625 625 $(self.editContainer).show();
626 626
627 627 });
628 628
629 629 $(this.previewButton).on('click', function(e) {
630 630 e.preventDefault();
631 631 var text = self.cm.getValue();
632 632
633 633 if (text === "") {
634 634 return;
635 635 }
636 636
637 637 var postData = {
638 638 'text': text,
639 639 'renderer': DEFAULT_RENDERER,
640 640 'csrf_token': CSRF_TOKEN
641 641 };
642 642
643 643 // lock ALL buttons on preview
644 644 self.setActionButtonsDisabled(true);
645 645
646 646 $(self.previewBoxSelector).addClass('unloaded');
647 647 $(self.previewBoxSelector).html(_gettext('Loading ...'));
648 648 $(self.editContainer).hide();
649 649 $(self.previewContainer).show();
650 650
651 651 // by default we reset state of comment preserving the text
652 652 var previewFailCallback = function(){
653 653 self.resetCommentFormState(text)
654 654 };
655 655 self.submitAjaxPOST(
656 656 self.previewUrl, postData, self.previewSuccessCallback, previewFailCallback);
657 657
658 658 });
659 659
660 660 $(this.submitForm).submit(function(e) {
661 661 e.preventDefault();
662 662 var allowedToSubmit = self.isAllowedToSubmit();
663 663 if (!allowedToSubmit){
664 664 return false;
665 665 }
666 666 self.handleFormSubmit();
667 667 });
668 668
669 669 }
670 670
671 671 return CommentForm;
672 672 })();
673
674 var CommentsController = function() { /* comments controller */
675 var self = this;
676
677 this.cancelComment = function(node) {
678 var $node = $(node);
679 var $td = $node.closest('td');
680 $node.closest('.comment-inline-form').removeClass('comment-inline-form-open');
681 return false;
682 }
683 this.getLineNumber = function(node) {
684 var $node = $(node);
685 return $node.closest('td').attr('data-line-number');
686 }
687 this.scrollToComment = function(node, offset) {
688 if (!node) {
689 node = $('.comment-selected');
690 if (!node.length) {
691 node = $('comment-current')
692 }
693 }
694 $comment = $(node).closest('.comment-current');
695 $comments = $('.comment-current');
696
697 $('.comment-selected').removeClass('comment-selected');
698
699 var nextIdx = $('.comment-current').index($comment) + offset;
700 if (nextIdx >= $comments.length) {
701 nextIdx = 0;
702 }
703 var $next = $('.comment-current').eq(nextIdx);
704 var $cb = $next.closest('.cb');
705 $cb.removeClass('cb-collapsed')
706
707 var $filediffCollapseState = $cb.closest('.filediff').prev();
708 $filediffCollapseState.prop('checked', false);
709 $next.addClass('comment-selected');
710 scrollToElement($next);
711 return false;
712 }
713 this.nextComment = function(node) {
714 return self.scrollToComment(node, 1);
715 }
716 this.prevComment = function(node) {
717 return self.scrollToComment(node, -1);
718 }
719 this.deleteComment = function(node) {
720 if (!confirm(_gettext('Delete this comment?'))) {
721 return false;
722 }
723 var $node = $(node);
724 var $td = $node.closest('td');
725 var $comment = $node.closest('.comment');
726 var comment_id = $comment.attr('data-comment-id');
727 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id);
728 var postData = {
729 '_method': 'delete',
730 'csrf_token': CSRF_TOKEN
731 };
732
733 $comment.addClass('comment-deleting');
734 $comment.hide('fast');
735
736 var success = function(response) {
737 $comment.remove();
738 return false;
739 };
740 var failure = function(data, textStatus, xhr) {
741 alert("error processing request: " + textStatus);
742 $comment.show('fast');
743 $comment.removeClass('comment-deleting');
744 return false;
745 };
746 ajaxPOST(url, postData, success, failure);
747 }
748 this.toggleComments = function(node, show) {
749 var $filediff = $(node).closest('.filediff');
750 if (show === true) {
751 $filediff.removeClass('hide-comments');
752 } else if (show === false) {
753 $filediff.find('.hide-line-comments').removeClass('hide-line-comments');
754 $filediff.addClass('hide-comments');
755 } else {
756 $filediff.find('.hide-line-comments').removeClass('hide-line-comments');
757 $filediff.toggleClass('hide-comments');
758 }
759 return false;
760 }
761 this.toggleLineComments = function(node) {
762 self.toggleComments(node, true);
763 var $node = $(node);
764 $node.closest('tr').toggleClass('hide-line-comments');
765 }
766 this.createComment = function(node) {
767 var $node = $(node);
768 var $td = $node.closest('td');
769 var $form = $td.find('.comment-inline-form');
770
771 if (!$form.length) {
772 var tmpl = $('#cb-comment-inline-form-template').html();
773 var $filediff = $node.closest('.filediff');
774 $filediff.removeClass('hide-comments');
775 var f_path = $filediff.attr('data-f-path');
776 var lineno = self.getLineNumber(node);
777 tmpl = tmpl.format(f_path, lineno);
778 $form = $(tmpl);
779
780 var $comments = $td.find('.inline-comments');
781 if (!$comments.length) {
782 $comments = $(
783 $('#cb-comments-inline-container-template').html());
784 $td.append($comments);
785 }
786
787 $td.find('.cb-comment-add-button').before($form);
788
789 var pullRequestId = templateContext.pull_request_data.pull_request_id;
790 var commitId = templateContext.commit_data.commit_id;
791 var _form = $form[0];
792 var commentForm = new CommentForm(_form, commitId, pullRequestId, lineno, false);
793 var cm = commentForm.getCmInstance();
794
795 // set a CUSTOM submit handler for inline comments.
796 commentForm.setHandleFormSubmit(function(o) {
797 var text = commentForm.cm.getValue();
798
799 if (text === "") {
800 return;
801 }
802
803 if (lineno === undefined) {
804 alert('missing line !');
805 return;
806 }
807 if (f_path === undefined) {
808 alert('missing file path !');
809 return;
810 }
811
812 var excludeCancelBtn = false;
813 var submitEvent = true;
814 commentForm.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
815 commentForm.cm.setOption("readOnly", true);
816 var postData = {
817 'text': text,
818 'f_path': f_path,
819 'line': lineno,
820 'csrf_token': CSRF_TOKEN
821 };
822 var submitSuccessCallback = function(json_data) {
823 $form.remove();
824 console.log(json_data)
825 try {
826 var html = json_data.rendered_text;
827 var lineno = json_data.line_no;
828 var target_id = json_data.target_id;
829
830 $comments.find('.cb-comment-add-button').before(html);
831 console.log(lineno, target_id, $comments);
832
833 } catch (e) {
834 console.error(e);
835 }
836
837
838 // re trigger the linkification of next/prev navigation
839 linkifyComments($('.inline-comment-injected'));
840 timeagoActivate();
841 bindDeleteCommentButtons();
842 commentForm.setActionButtonsDisabled(false);
843
844 };
845 var submitFailCallback = function(){
846 commentForm.resetCommentFormState(text)
847 };
848 commentForm.submitAjaxPOST(
849 commentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback);
850 });
851
852 setTimeout(function() {
853 // callbacks
854 if (cm !== undefined) {
855 cm.focus();
856 }
857 }, 10);
858
859 $.Topic('/ui/plugins/code/comment_form_built').prepareOrPublish({
860 form: _form,
861 parent: $td[0],
862 lineno: lineno,
863 f_path: f_path}
864 );
865 }
866
867 $form.addClass('comment-inline-form-open');
868 }
869
870 this.renderInlineComments = function(file_comments) {
871 show_add_button = typeof show_add_button !== 'undefined' ? show_add_button : true;
872
873 for (var i = 0; i < file_comments.length; i++) {
874 var box = file_comments[i];
875
876 var target_id = $(box).attr('target_id');
877
878 // actually comments with line numbers
879 var comments = box.children;
880
881 for (var j = 0; j < comments.length; j++) {
882 var data = {
883 'rendered_text': comments[j].outerHTML,
884 'line_no': $(comments[j]).attr('line'),
885 'target_id': target_id
886 };
887 }
888 }
889
890 // since order of injection is random, we're now re-iterating
891 // from correct order and filling in links
892 linkifyComments($('.inline-comment-injected'));
893 bindDeleteCommentButtons();
894 firefoxAnchorFix();
895 };
896
897 } No newline at end of file
@@ -1,387 +1,183 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <!DOCTYPE html>
3 3
4 4 <%
5 5 c.template_context['repo_name'] = getattr(c, 'repo_name', '')
6 6
7 7 if hasattr(c, 'rhodecode_db_repo'):
8 8 c.template_context['repo_type'] = c.rhodecode_db_repo.repo_type
9 9 c.template_context['repo_landing_commit'] = c.rhodecode_db_repo.landing_rev[1]
10 10
11 11 if getattr(c, 'rhodecode_user', None) and c.rhodecode_user.user_id:
12 12 c.template_context['rhodecode_user']['username'] = c.rhodecode_user.username
13 13 c.template_context['rhodecode_user']['email'] = c.rhodecode_user.email
14 14 c.template_context['rhodecode_user']['notification_status'] = c.rhodecode_user.get_instance().user_data.get('notification_status', True)
15 15 c.template_context['rhodecode_user']['first_name'] = c.rhodecode_user.name
16 16 c.template_context['rhodecode_user']['last_name'] = c.rhodecode_user.lastname
17 17
18 18 c.template_context['visual']['default_renderer'] = h.get_visual_attr(c, 'default_renderer')
19 19 %>
20 20 <html xmlns="http://www.w3.org/1999/xhtml">
21 21 <head>
22 22 <script src="${h.asset('js/vendors/webcomponentsjs/webcomponents-lite.min.js', ver=c.rhodecode_version_hash)}"></script>
23 23 <link rel="import" href="${h.asset('js/rhodecode-components.html', ver=c.rhodecode_version_hash)}">
24 24 <title>${self.title()}</title>
25 25 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
26 26 <%def name="robots()">
27 27 <meta name="robots" content="index, nofollow"/>
28 28 </%def>
29 29 ${self.robots()}
30 30 <link rel="icon" href="${h.asset('images/favicon.ico', ver=c.rhodecode_version_hash)}" sizes="16x16 32x32" type="image/png" />
31 31
32 32 ## CSS definitions
33 33 <%def name="css()">
34 34 <link rel="stylesheet" type="text/css" href="${h.asset('css/style.css', ver=c.rhodecode_version_hash)}" media="screen"/>
35 35 <!--[if lt IE 9]>
36 36 <link rel="stylesheet" type="text/css" href="${h.asset('css/ie.css', ver=c.rhodecode_version_hash)}" media="screen"/>
37 37 <![endif]-->
38 38 ## EXTRA FOR CSS
39 39 ${self.css_extra()}
40 40 </%def>
41 41 ## CSS EXTRA - optionally inject some extra CSS stuff needed for specific websites
42 42 <%def name="css_extra()">
43 43 </%def>
44 44
45 45 ${self.css()}
46 46
47 47 ## JAVASCRIPT
48 48 <%def name="js()">
49 49 <script>
50 50 // setup Polymer options
51 51 window.Polymer = {lazyRegister: true, dom: 'shadow'};
52 52
53 53 // Load webcomponentsjs polyfill if browser does not support native Web Components
54 54 (function() {
55 55 'use strict';
56 56 var onload = function() {
57 57 // For native Imports, manually fire WebComponentsReady so user code
58 58 // can use the same code path for native and polyfill'd imports.
59 59 if (!window.HTMLImports) {
60 60 document.dispatchEvent(
61 61 new CustomEvent('WebComponentsReady', {bubbles: true})
62 62 );
63 63 }
64 64 };
65 65 var webComponentsSupported = (
66 66 'registerElement' in document
67 67 && 'import' in document.createElement('link')
68 68 && 'content' in document.createElement('template')
69 69 );
70 70 if (!webComponentsSupported) {
71 71 } else {
72 72 onload();
73 73 }
74 74 })();
75 75 </script>
76 76
77 77 <script src="${h.asset('js/rhodecode/i18n/%s.js' % c.language, ver=c.rhodecode_version_hash)}"></script>
78 78 <script type="text/javascript">
79 79 // register templateContext to pass template variables to JS
80 80 var templateContext = ${h.json.dumps(c.template_context)|n};
81 81
82 82 var REPO_NAME = "${getattr(c, 'repo_name', '')}";
83 83 %if hasattr(c, 'rhodecode_db_repo'):
84 84 var REPO_LANDING_REV = '${c.rhodecode_db_repo.landing_rev[1]}';
85 85 var REPO_TYPE = '${c.rhodecode_db_repo.repo_type}';
86 86 %else:
87 87 var REPO_LANDING_REV = '';
88 88 var REPO_TYPE = '';
89 89 %endif
90 90 var APPLICATION_URL = "${h.url('home').rstrip('/')}";
91 91 var ASSET_URL = "${h.asset('')}";
92 92 var DEFAULT_RENDERER = "${h.get_visual_attr(c, 'default_renderer')}";
93 93 var CSRF_TOKEN = "${getattr(c, 'csrf_token', '')}";
94 94 % if getattr(c, 'rhodecode_user', None):
95 95 var USER = {name:'${c.rhodecode_user.username}'};
96 96 % else:
97 97 var USER = {name:null};
98 98 % endif
99 99
100 100 var APPENLIGHT = {
101 101 enabled: ${'true' if getattr(c, 'appenlight_enabled', False) else 'false'},
102 102 key: '${getattr(c, "appenlight_api_public_key", "")}',
103 103 % if getattr(c, 'appenlight_server_url', None):
104 104 serverUrl: '${getattr(c, "appenlight_server_url", "")}',
105 105 % endif
106 106 requestInfo: {
107 107 % if getattr(c, 'rhodecode_user', None):
108 108 ip: '${c.rhodecode_user.ip_addr}',
109 109 username: '${c.rhodecode_user.username}'
110 110 % endif
111 111 },
112 112 tags: {
113 113 rhodecode_version: '${c.rhodecode_version}',
114 114 rhodecode_edition: '${c.rhodecode_edition}'
115 115 }
116 116 };
117 117
118
119 Rhodecode = (function() {
120 function _Rhodecode() {
121 this.comments = new (function() { /* comments controller */
122 var self = this;
123
124 this.cancelComment = function(node) {
125 var $node = $(node);
126 var $td = $node.closest('td');
127 $node.closest('.comment-inline-form').removeClass('comment-inline-form-open');
128 return false;
129 }
130 this.getLineNumber = function(node) {
131 var $node = $(node);
132 return $node.closest('td').attr('data-line-number');
133 }
134 this.scrollToComment = function(node, offset) {
135 if (!node) {
136 node = $('.comment-selected');
137 if (!node.length) {
138 node = $('comment-current')
139 }
140 }
141 $comment = $(node).closest('.comment-current');
142 $comments = $('.comment-current');
143
144 $('.comment-selected').removeClass('comment-selected');
145
146 var nextIdx = $('.comment-current').index($comment) + offset;
147 if (nextIdx >= $comments.length) {
148 nextIdx = 0;
149 }
150 var $next = $('.comment-current').eq(nextIdx);
151 var $cb = $next.closest('.cb');
152 $cb.removeClass('cb-collapsed')
153
154 var $filediffCollapseState = $cb.closest('.filediff').prev();
155 $filediffCollapseState.prop('checked', false);
156 $next.addClass('comment-selected');
157 scrollToElement($next);
158 return false;
159 }
160 this.nextComment = function(node) {
161 return self.scrollToComment(node, 1);
162 }
163 this.prevComment = function(node) {
164 return self.scrollToComment(node, -1);
165 }
166 this.deleteComment = function(node) {
167 if (!confirm(_gettext('Delete this comment?'))) {
168 return false;
169 }
170 var $node = $(node);
171 var $td = $node.closest('td');
172 var $comment = $node.closest('.comment');
173 var comment_id = $comment.attr('data-comment-id');
174 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id);
175 var postData = {
176 '_method': 'delete',
177 'csrf_token': CSRF_TOKEN
178 };
179
180 $comment.addClass('comment-deleting');
181 $comment.hide('fast');
182
183 var success = function(response) {
184 $comment.remove();
185 return false;
186 };
187 var failure = function(data, textStatus, xhr) {
188 alert("error processing request: " + textStatus);
189 $comment.show('fast');
190 $comment.removeClass('comment-deleting');
191 return false;
192 };
193 ajaxPOST(url, postData, success, failure);
194 }
195 this.createComment = function(node) {
196 var $node = $(node);
197 var $td = $node.closest('td');
198 var $form = $td.find('.comment-inline-form');
199
200 if (!$form.length) {
201 var tmpl = $('#cb-comment-inline-form-template').html();
202 var f_path = $node.closest('.filediff').attr('data-f-path');
203 var lineno = self.getLineNumber(node);
204 tmpl = tmpl.format(f_path, lineno);
205 $form = $(tmpl);
206
207 var $comments = $td.find('.inline-comments');
208 if (!$comments.length) {
209 $comments = $(
210 $('#cb-comments-inline-container-template').html());
211 $td.append($comments);
212 }
213
214 $td.find('.cb-comment-add-button').before($form);
215
216 var pullRequestId = templateContext.pull_request_data.pull_request_id;
217 var commitId = templateContext.commit_data.commit_id;
218 var _form = $form[0];
219 var commentForm = new CommentForm(_form, commitId, pullRequestId, lineno, false);
220 var cm = commentForm.getCmInstance();
221
222 // set a CUSTOM submit handler for inline comments.
223 commentForm.setHandleFormSubmit(function(o) {
224 var text = commentForm.cm.getValue();
225
226 if (text === "") {
227 return;
228 }
229
230 if (lineno === undefined) {
231 alert('missing line !');
232 return;
233 }
234 if (f_path === undefined) {
235 alert('missing file path !');
236 return;
237 }
238
239 var excludeCancelBtn = false;
240 var submitEvent = true;
241 commentForm.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
242 commentForm.cm.setOption("readOnly", true);
243 var postData = {
244 'text': text,
245 'f_path': f_path,
246 'line': lineno,
247 'csrf_token': CSRF_TOKEN
248 };
249 var submitSuccessCallback = function(json_data) {
250 $form.remove();
251 console.log(json_data)
252 try {
253 var html = json_data.rendered_text;
254 var lineno = json_data.line_no;
255 var target_id = json_data.target_id;
256
257 $comments.find('.cb-comment-add-button').before(html);
258 console.log(lineno, target_id, $comments);
259
260 } catch (e) {
261 console.error(e);
262 }
263
264
265 // re trigger the linkification of next/prev navigation
266 linkifyComments($('.inline-comment-injected'));
267 timeagoActivate();
268 bindDeleteCommentButtons();
269 commentForm.setActionButtonsDisabled(false);
270
271 };
272 var submitFailCallback = function(){
273 commentForm.resetCommentFormState(text)
274 };
275 commentForm.submitAjaxPOST(
276 commentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback);
277 });
278
279 setTimeout(function() {
280 // callbacks
281 if (cm !== undefined) {
282 cm.focus();
283 }
284 }, 10);
285
286 $.Topic('/ui/plugins/code/comment_form_built').prepareOrPublish({
287 form: _form,
288 parent: $td[0],
289 lineno: lineno,
290 f_path: f_path}
291 );
292 }
293
294 $form.addClass('comment-inline-form-open');
295 }
296
297 this.renderInlineComments = function(file_comments) {
298 show_add_button = typeof show_add_button !== 'undefined' ? show_add_button : true;
299
300 for (var i = 0; i < file_comments.length; i++) {
301 var box = file_comments[i];
302
303 var target_id = $(box).attr('target_id');
304
305 // actually comments with line numbers
306 var comments = box.children;
307
308 for (var j = 0; j < comments.length; j++) {
309 var data = {
310 'rendered_text': comments[j].outerHTML,
311 'line_no': $(comments[j]).attr('line'),
312 'target_id': target_id
313 };
314 }
315 }
316
317 // since order of injection is random, we're now re-iterating
318 // from correct order and filling in links
319 linkifyComments($('.inline-comment-injected'));
320 bindDeleteCommentButtons();
321 firefoxAnchorFix();
322 };
323
324 })();
325 }
326 return new _Rhodecode();
327 })();
328
329 118 </script>
330 119 <%include file="/base/plugins_base.html"/>
331 120 <!--[if lt IE 9]>
332 121 <script language="javascript" type="text/javascript" src="${h.asset('js/excanvas.min.js')}"></script>
333 122 <![endif]-->
334 123 <script language="javascript" type="text/javascript" src="${h.asset('js/rhodecode/routes.js', ver=c.rhodecode_version_hash)}"></script>
335 124 <script> var alertMessagePayloads = ${h.flash.json_alerts()|n}; </script>
336 125 ## avoide escaping the %N
337 126 <script language="javascript" type="text/javascript" src="${h.asset('js/rhodecode-components.js', ver=c.rhodecode_version_hash)}"></script>
338 127 <script>CodeMirror.modeURL = "${h.asset('') + 'js/mode/%N/%N.js?ver='+c.rhodecode_version_hash}";</script>
339 128
340 129
341 130 ## JAVASCRIPT EXTRA - optionally inject some extra JS for specificed templates
342 131 ${self.js_extra()}
343 132
344 133 <script type="text/javascript">
134 Rhodecode = (function() {
135 function _Rhodecode() {
136 this.comments = new CommentsController();
137 }
138 return new _Rhodecode();
139 })();
140
345 141 $(document).ready(function(){
346 142 show_more_event();
347 143 timeagoActivate();
348 144 })
349 145 </script>
350 146
351 147 </%def>
352 148
353 149 ## JAVASCRIPT EXTRA - optionally inject some extra JS for specificed templates
354 150 <%def name="js_extra()"></%def>
355 151 ${self.js()}
356 152
357 153 <%def name="head_extra()"></%def>
358 154 ${self.head_extra()}
359 155 ## extra stuff
360 156 %if c.pre_code:
361 157 ${c.pre_code|n}
362 158 %endif
363 159 </head>
364 160 <body id="body">
365 161 <noscript>
366 162 <div class="noscript-error">
367 163 ${_('Please enable JavaScript to use RhodeCode Enterprise')}
368 164 </div>
369 165 </noscript>
370 166 ## IE hacks
371 167 <!--[if IE 7]>
372 168 <script>$(document.body).addClass('ie7')</script>
373 169 <![endif]-->
374 170 <!--[if IE 8]>
375 171 <script>$(document.body).addClass('ie8')</script>
376 172 <![endif]-->
377 173 <!--[if IE 9]>
378 174 <script>$(document.body).addClass('ie9')</script>
379 175 <![endif]-->
380 176
381 177 ${next.body()}
382 178 %if c.post_code:
383 179 ${c.post_code|n}
384 180 %endif
385 181 <rhodecode-app></rhodecode-app>
386 182 </body>
387 183 </html>
@@ -1,542 +1,567 b''
1 1 <%def name="diff_line_anchor(filename, line, type)"><%
2 2 return '%s_%s_%i' % (h.safeid(filename), type, line)
3 3 %></%def>
4 4
5 5 <%def name="action_class(action)"><%
6 6 return {
7 7 '-': 'cb-deletion',
8 8 '+': 'cb-addition',
9 9 ' ': 'cb-context',
10 10 }.get(action, 'cb-empty')
11 11 %></%def>
12 12
13 13 <%def name="op_class(op_id)"><%
14 14 return {
15 15 DEL_FILENODE: 'deletion', # file deleted
16 16 BIN_FILENODE: 'warning' # binary diff hidden
17 17 }.get(op_id, 'addition')
18 18 %></%def>
19 19
20 20 <%def name="link_for(**kw)"><%
21 21 new_args = request.GET.mixed()
22 22 new_args.update(kw)
23 23 return h.url('', **new_args)
24 24 %></%def>
25 25
26 26 <%def name="render_diffset(diffset, commit=None,
27 27
28 28 # collapse all file diff entries when there are more than this amount of files in the diff
29 29 collapse_when_files_over=20,
30 30
31 31 # collapse lines in the diff when more than this amount of lines changed in the file diff
32 32 lines_changed_limit=500,
33 33
34 34 # add a ruler at to the output
35 35 ruler_at_chars=0,
36 36
37 37 # turn on inline comments
38 38 use_comments=False,
39 39
40 40 )">
41 41
42 42 %if use_comments:
43 43 <div id="cb-comments-inline-container-template" class="js-template">
44 44 ${inline_comments_container([])}
45 45 </div>
46 46 <div class="js-template" id="cb-comment-inline-form-template">
47 47 <div class="comment-inline-form ac">
48 48 %if c.rhodecode_user.username != h.DEFAULT_USER:
49 49 ${h.form('#', method='get')}
50 50 <div id="edit-container_{1}" class="clearfix">
51 51 <div class="comment-title pull-left">
52 52 ${_('Create a comment on line {1}.')}
53 53 </div>
54 54 <div class="comment-help pull-right">
55 55 ${(_('Comments parsed using %s syntax with %s support.') % (
56 56 ('<a href="%s">%s</a>' % (h.url('%s_help' % c.visual.default_renderer), c.visual.default_renderer.upper())),
57 57 ('<span class="tooltip" title="%s">@mention</span>' % _('Use @username inside this text to send notification to this RhodeCode user'))
58 58 )
59 59 )|n
60 60 }
61 61 </div>
62 62 <div style="clear: both"></div>
63 63 <textarea id="text_{1}" name="text" class="comment-block-ta ac-input"></textarea>
64 64 </div>
65 65 <div id="preview-container_{1}" class="clearfix" style="display: none;">
66 66 <div class="comment-help">
67 67 ${_('Comment preview')}
68 68 </div>
69 69 <div id="preview-box_{1}" class="preview-box"></div>
70 70 </div>
71 71 <div class="comment-footer">
72 72 <div class="action-buttons">
73 73 <input type="hidden" name="f_path" value="{0}">
74 74 <input type="hidden" name="line" value="{1}">
75 75 <button id="preview-btn_{1}" class="btn btn-secondary">${_('Preview')}</button>
76 76 <button id="edit-btn_{1}" class="btn btn-secondary" style="display: none;">${_('Edit')}</button>
77 77 ${h.submit('save', _('Comment'), class_='btn btn-success save-inline-form')}
78 78 </div>
79 79 <div class="comment-button">
80 80 <button type="button" class="cb-comment-cancel" onclick="return Rhodecode.comments.cancelComment(this);">
81 81 ${_('Cancel')}
82 82 </button>
83 83 </div>
84 84 ${h.end_form()}
85 85 </div>
86 86 %else:
87 87 ${h.form('', class_='inline-form comment-form-login', method='get')}
88 88 <div class="pull-left">
89 89 <div class="comment-help pull-right">
90 90 ${_('You need to be logged in to comment.')} <a href="${h.route_path('login', _query={'came_from': h.url.current()})}">${_('Login now')}</a>
91 91 </div>
92 92 </div>
93 93 <div class="comment-button pull-right">
94 94 <button type="button" class="cb-comment-cancel" onclick="return Rhodecode.comments.cancelComment(this);">
95 95 ${_('Cancel')}
96 96 </button>
97 97 </div>
98 98 <div class="clearfix"></div>
99 99 ${h.end_form()}
100 100 %endif
101 101 </div>
102 102 </div>
103 103
104 104 %endif
105 105 <%
106 106 collapse_all = len(diffset.files) > collapse_when_files_over
107 107 %>
108 108
109 109 %if c.diffmode == 'sideside':
110 110 <style>
111 111 .wrapper {
112 112 max-width: 1600px !important;
113 113 }
114 114 </style>
115 115 %endif
116 116 %if ruler_at_chars:
117 117 <style>
118 118 .diff table.cb .cb-content:after {
119 119 content: "";
120 120 border-left: 1px solid blue;
121 121 position: absolute;
122 122 top: 0;
123 123 height: 18px;
124 124 opacity: .2;
125 125 z-index: 10;
126 126 ## +5 to account for diff action (+/-)
127 127 left: ${ruler_at_chars + 5}ch;
128 128 </style>
129 129 %endif
130 130
131 131 <div class="diffset">
132 132 <div class="diffset-heading ${diffset.limited_diff and 'diffset-heading-warning' or ''}">
133 133 %if commit:
134 134 <div class="pull-right">
135 135 <a class="btn tooltip" title="${_('Browse Files at revision {}').format(commit.raw_id)}" href="${h.url('files_home',repo_name=diffset.repo_name, revision=commit.raw_id, f_path='')}">
136 136 ${_('Browse Files')}
137 137 </a>
138 138 </div>
139 139 %endif
140 140 <h2 class="clearinner">
141 141 %if commit:
142 142 <a class="tooltip revision" title="${h.tooltip(commit.message)}" href="${h.url('changeset_home',repo_name=c.repo_name,revision=commit.raw_id)}">${'r%s:%s' % (commit.revision,h.short_id(commit.raw_id))}</a> -
143 143 ${h.age_component(commit.date)} -
144 144 %endif
145 145 %if diffset.limited_diff:
146 146 ${_('The requested commit is too big and content was truncated.')}
147 147
148 148 ${ungettext('%(num)s file changed.', '%(num)s files changed.', diffset.changed_files) % {'num': diffset.changed_files}}
149 149 <a href="${link_for(fulldiff=1)}" onclick="return confirm('${_("Showing a big diff might take some time and resources, continue?")}')">${_('Show full diff')}</a>
150 150 %else:
151 151 ${ungettext('%(num)s file changed: %(linesadd)s inserted, ''%(linesdel)s deleted',
152 152 '%(num)s files changed: %(linesadd)s inserted, %(linesdel)s deleted', diffset.changed_files) % {'num': diffset.changed_files, 'linesadd': diffset.lines_added, 'linesdel': diffset.lines_deleted}}
153 153 %endif
154 154 </h2>
155 155 </div>
156 156
157 157 %if not diffset.files:
158 158 <p class="empty_data">${_('No files')}</p>
159 159 %endif
160 160
161 161 <div class="filediffs">
162 162 %for i, filediff in enumerate(diffset.files):
163 163 <%
164 164 lines_changed = filediff['patch']['stats']['added'] + filediff['patch']['stats']['deleted']
165 165 over_lines_changed_limit = lines_changed > lines_changed_limit
166 166 %>
167 167 <input ${collapse_all and 'checked' or ''} class="filediff-collapse-state" id="filediff-collapse-${id(filediff)}" type="checkbox">
168 168 <div
169 169 class="filediff"
170 170 data-f-path="${filediff['patch']['filename']}"
171 171 id="a_${h.FID('', filediff['patch']['filename'])}">
172 172 <label for="filediff-collapse-${id(filediff)}" class="filediff-heading">
173 173 <div class="filediff-collapse-indicator"></div>
174 174 ${diff_ops(filediff)}
175 175 </label>
176 176 ${diff_menu(filediff, use_comments=use_comments)}
177 177 <table class="cb cb-diff-${c.diffmode} code-highlight ${over_lines_changed_limit and 'cb-collapsed' or ''}">
178 178 %if not filediff.hunks:
179 179 %for op_id, op_text in filediff['patch']['stats']['ops'].items():
180 180 <tr>
181 181 <td class="cb-text cb-${op_class(op_id)}" ${c.diffmode == 'unified' and 'colspan=3' or 'colspan=4'}>
182 182 %if op_id == DEL_FILENODE:
183 183 ${_('File was deleted')}
184 184 %elif op_id == BIN_FILENODE:
185 185 ${_('Binary file hidden')}
186 186 %else:
187 187 ${op_text}
188 188 %endif
189 189 </td>
190 190 </tr>
191 191 %endfor
192 192 %endif
193 193 %if over_lines_changed_limit:
194 194 <tr class="cb-warning cb-collapser">
195 <td class="cb-text" ${c.diffmode == 'unified' and 'colspan=3' or 'colspan=4'}>
195 <td class="cb-text" ${c.diffmode == 'unified' and 'colspan=4' or 'colspan=4'}>
196 196 ${_('This diff has been collapsed as it changes many lines, (%i lines changed)' % lines_changed)}
197 197 <a href="#" class="cb-expand"
198 198 onclick="$(this).closest('table').removeClass('cb-collapsed'); return false;">${_('Show them')}
199 199 </a>
200 200 <a href="#" class="cb-collapse"
201 201 onclick="$(this).closest('table').addClass('cb-collapsed'); return false;">${_('Hide them')}
202 202 </a>
203 203 </td>
204 204 </tr>
205 205 %endif
206 206 %if filediff.patch['is_limited_diff']:
207 207 <tr class="cb-warning cb-collapser">
208 <td class="cb-text" ${c.diffmode == 'unified' and 'colspan=3' or 'colspan=4'}>
208 <td class="cb-text" ${c.diffmode == 'unified' and 'colspan=4' or 'colspan=4'}>
209 209 ${_('The requested commit is too big and content was truncated.')} <a href="${link_for(fulldiff=1)}" onclick="return confirm('${_("Showing a big diff might take some time and resources, continue?")}')">${_('Show full diff')}</a>
210 210 </td>
211 211 </tr>
212 212 %endif
213 213 %for hunk in filediff.hunks:
214 214 <tr class="cb-hunk">
215 <td ${c.diffmode == 'unified' and 'colspan=2' or ''}>
215 <td ${c.diffmode == 'unified' and 'colspan=3' or ''}>
216 216 ## TODO: dan: add ajax loading of more context here
217 217 ## <a href="#">
218 218 <i class="icon-more"></i>
219 219 ## </a>
220 220 </td>
221 <td ${c.diffmode == 'sideside' and 'colspan=3' or ''}>
221 <td ${c.diffmode == 'sideside' and 'colspan=5' or ''}>
222 222 @@
223 223 -${hunk.source_start},${hunk.source_length}
224 224 +${hunk.target_start},${hunk.target_length}
225 225 ${hunk.section_header}
226 226 </td>
227 227 </tr>
228 228 %if c.diffmode == 'unified':
229 229 ${render_hunk_lines_unified(hunk, use_comments=use_comments)}
230 230 %elif c.diffmode == 'sideside':
231 231 ${render_hunk_lines_sideside(hunk, use_comments=use_comments)}
232 232 %else:
233 233 <tr class="cb-line">
234 234 <td>unknown diff mode</td>
235 235 </tr>
236 236 %endif
237 237 %endfor
238 238 </table>
239 239 </div>
240 240 %endfor
241 241 </div>
242 242 </div>
243 243 </%def>
244 244
245 245 <%def name="diff_ops(filediff)">
246 246 <%
247 247 stats = filediff['patch']['stats']
248 248 from rhodecode.lib.diffs import NEW_FILENODE, DEL_FILENODE, \
249 249 MOD_FILENODE, RENAMED_FILENODE, CHMOD_FILENODE, BIN_FILENODE
250 250 %>
251 251 <span class="pill">
252 252 %if filediff.source_file_path and filediff.target_file_path:
253 253 %if filediff.source_file_path != filediff.target_file_path: # file was renamed
254 254 <strong>${filediff.target_file_path}</strong> β¬… <del>${filediff.source_file_path}</del>
255 255 %else:
256 256 ## file was modified
257 257 <strong>${filediff.source_file_path}</strong>
258 258 %endif
259 259 %else:
260 260 %if filediff.source_file_path:
261 261 ## file was deleted
262 262 <strong>${filediff.source_file_path}</strong>
263 263 %else:
264 264 ## file was added
265 265 <strong>${filediff.target_file_path}</strong>
266 266 %endif
267 267 %endif
268 268 </span>
269 269 <span class="pill-group" style="float: left">
270 270 %if filediff.patch['is_limited_diff']:
271 271 <span class="pill tooltip" op="limited" title="The stats for this diff are not complete">limited diff</span>
272 272 %endif
273 273 %if RENAMED_FILENODE in stats['ops']:
274 274 <span class="pill" op="renamed">renamed</span>
275 275 %endif
276 276
277 277 %if NEW_FILENODE in stats['ops']:
278 278 <span class="pill" op="created">created</span>
279 279 %if filediff['target_mode'].startswith('120'):
280 280 <span class="pill" op="symlink">symlink</span>
281 281 %else:
282 282 <span class="pill" op="mode">${nice_mode(filediff['target_mode'])}</span>
283 283 %endif
284 284 %endif
285 285
286 286 %if DEL_FILENODE in stats['ops']:
287 287 <span class="pill" op="removed">removed</span>
288 288 %endif
289 289
290 290 %if CHMOD_FILENODE in stats['ops']:
291 291 <span class="pill" op="mode">
292 292 ${nice_mode(filediff['source_mode'])} ➑ ${nice_mode(filediff['target_mode'])}
293 293 </span>
294 294 %endif
295 295 </span>
296 296
297 297 <a class="pill filediff-anchor" href="#a_${h.FID('', filediff.patch['filename'])}">ΒΆ</a>
298 298
299 299 <span class="pill-group" style="float: right">
300 300 %if BIN_FILENODE in stats['ops']:
301 301 <span class="pill" op="binary">binary</span>
302 302 %if MOD_FILENODE in stats['ops']:
303 303 <span class="pill" op="modified">modified</span>
304 304 %endif
305 305 %endif
306 306 %if stats['added']:
307 307 <span class="pill" op="added">+${stats['added']}</span>
308 308 %endif
309 309 %if stats['deleted']:
310 310 <span class="pill" op="deleted">-${stats['deleted']}</span>
311 311 %endif
312 312 </span>
313 313
314 314 </%def>
315 315
316 316 <%def name="nice_mode(filemode)">
317 317 ${filemode.startswith('100') and filemode[3:] or filemode}
318 318 </%def>
319 319
320 320 <%def name="diff_menu(filediff, use_comments=False)">
321 321 <div class="filediff-menu">
322 322 %if filediff.diffset.source_ref:
323 323 %if filediff.patch['operation'] in ['D', 'M']:
324 324 <a
325 325 class="tooltip"
326 326 href="${h.url('files_home',repo_name=filediff.diffset.repo_name,f_path=filediff.source_file_path,revision=filediff.diffset.source_ref)}"
327 327 title="${h.tooltip(_('Show file at commit: %(commit_id)s') % {'commit_id': filediff.diffset.source_ref[:12]})}"
328 328 >
329 329 ${_('Show file before')}
330 330 </a>
331 331 %else:
332 332 <span
333 333 class="tooltip"
334 334 title="${h.tooltip(_('File no longer present at commit: %(commit_id)s') % {'commit_id': filediff.diffset.source_ref[:12]})}"
335 335 >
336 336 ${_('Show file before')}
337 337 </span>
338 338 %endif
339 339 %if filediff.patch['operation'] in ['A', 'M']:
340 340 <a
341 341 class="tooltip"
342 342 href="${h.url('files_home',repo_name=filediff.diffset.repo_name,f_path=filediff.target_file_path,revision=filediff.diffset.target_ref)}"
343 343 title="${h.tooltip(_('Show file at commit: %(commit_id)s') % {'commit_id': filediff.diffset.target_ref[:12]})}"
344 344 >
345 345 ${_('Show file after')}
346 346 </a>
347 347 %else:
348 348 <span
349 349 class="tooltip"
350 350 title="${h.tooltip(_('File no longer present at commit: %(commit_id)s') % {'commit_id': filediff.diffset.target_ref[:12]})}"
351 351 >
352 352 ${_('Show file after')}
353 353 </span>
354 354 %endif
355 355 <a
356 356 class="tooltip"
357 357 title="${h.tooltip(_('Raw diff'))}"
358 358 href="${h.url('files_diff_home',repo_name=filediff.diffset.repo_name,f_path=filediff.target_file_path,diff2=filediff.diffset.target_ref,diff1=filediff.diffset.source_ref,diff='raw')}"
359 359 >
360 360 ${_('Raw diff')}
361 361 </a>
362 362 <a
363 363 class="tooltip"
364 364 title="${h.tooltip(_('Download diff'))}"
365 365 href="${h.url('files_diff_home',repo_name=filediff.diffset.repo_name,f_path=filediff.target_file_path,diff2=filediff.diffset.target_ref,diff1=filediff.diffset.source_ref,diff='download')}"
366 366 >
367 367 ${_('Download diff')}
368 368 </a>
369 369
370 370 ## TODO: dan: refactor ignorews_url and context_url into the diff renderer same as diffmode=unified/sideside. Also use ajax to load more context (by clicking hunks)
371 371 %if hasattr(c, 'ignorews_url'):
372 372 ${c.ignorews_url(request.GET, h.FID('', filediff['patch']['filename']))}
373 373 %endif
374 374 %if hasattr(c, 'context_url'):
375 375 ${c.context_url(request.GET, h.FID('', filediff['patch']['filename']))}
376 376 %endif
377 377
378 378
379 379 %if use_comments:
380 <a href="#" onclick="$(this).closest('.filediff').toggleClass('hide-comments'); return false;">
380 <a href="#" onclick="return Rhodecode.comments.toggleComments(this);">
381 381 <span class="show-comment-button">${_('Show comments')}</span><span class="hide-comment-button">${_('Hide comments')}</span>
382 382 </a>
383 383 %endif
384 384 %endif
385 385 </div>
386 386 </%def>
387 387
388 388
389 389 <%namespace name="commentblock" file="/changeset/changeset_file_comment.html"/>
390 390 <%def name="inline_comments_container(comments)">
391 391 <div class="inline-comments">
392 392 %for comment in comments:
393 393 ${commentblock.comment_block(comment, inline=True)}
394 394 %endfor
395 395 <span onclick="return Rhodecode.comments.createComment(this)"
396 396 class="btn btn-secondary cb-comment-add-button">
397 397 ${_('Add another comment')}
398 398 </span>
399 399 </div>
400 400 </%def>
401 401
402 402
403 403 <%def name="render_hunk_lines_sideside(hunk, use_comments=False)">
404 404 %for i, line in enumerate(hunk.sideside):
405 405 <%
406 406 old_line_anchor, new_line_anchor = None, None
407 407 if line.original.lineno:
408 408 old_line_anchor = diff_line_anchor(hunk.filediff.source_file_path, line.original.lineno, 'o')
409 409 if line.modified.lineno:
410 410 new_line_anchor = diff_line_anchor(hunk.filediff.target_file_path, line.modified.lineno, 'n')
411 411 %>
412 412 <tr class="cb-line">
413 <td class="cb-data ${action_class(line.original.action)}"
414 data-line-number="${line.original.lineno}"
415 >
416 <div>
417 %if line.original.comments:
418 <i class="icon-comment" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
419 %endif
420 </div>
421 </td>
413 422 <td class="cb-lineno ${action_class(line.original.action)}"
414 423 data-line-number="${line.original.lineno}"
415 424 %if old_line_anchor:
416 425 id="${old_line_anchor}"
417 426 %endif
418 427 >
419 428 %if line.original.lineno:
420 429 <a name="${old_line_anchor}" href="#${old_line_anchor}">${line.original.lineno}</a>
421 430 %endif
422 431 </td>
423 432 <td class="cb-content ${action_class(line.original.action)}"
424 433 data-line-number="o${line.original.lineno}"
425 434 >
426 435 %if use_comments and line.original.lineno:
427 436 ${render_add_comment_button()}
428 437 %endif
429 438 <span class="cb-code">${line.original.action} ${line.original.content or '' | n}</span>
430 439 %if use_comments and line.original.lineno and line.original.comments:
431 440 ${inline_comments_container(line.original.comments)}
432 441 %endif
433 442 </td>
443 <td class="cb-data ${action_class(line.modified.action)}"
444 data-line-number="${line.modified.lineno}"
445 >
446 <div>
447 %if line.modified.comments:
448 <i class="icon-comment" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
449 %endif
450 </div>
451 </td>
434 452 <td class="cb-lineno ${action_class(line.modified.action)}"
435 453 data-line-number="${line.modified.lineno}"
436 454 %if new_line_anchor:
437 455 id="${new_line_anchor}"
438 456 %endif
439 457 >
440 458 %if line.modified.lineno:
441 459 <a name="${new_line_anchor}" href="#${new_line_anchor}">${line.modified.lineno}</a>
442 460 %endif
443 </td>
461 </td>
444 462 <td class="cb-content ${action_class(line.modified.action)}"
445 463 data-line-number="n${line.modified.lineno}"
446 464 >
447 465 %if use_comments and line.modified.lineno:
448 466 ${render_add_comment_button()}
449 467 %endif
450 468 <span class="cb-code">${line.modified.action} ${line.modified.content or '' | n}</span>
451 469 %if use_comments and line.modified.lineno and line.modified.comments:
452 470 ${inline_comments_container(line.modified.comments)}
453 471 %endif
454 472 </td>
455 473 </tr>
456 474 %endfor
457 475 </%def>
458 476
459 477
460 478 <%def name="render_hunk_lines_unified(hunk, use_comments=False)">
461 479 %for old_line_no, new_line_no, action, content, comments in hunk.unified:
462 480 <%
463 481 old_line_anchor, new_line_anchor = None, None
464 482 if old_line_no:
465 483 old_line_anchor = diff_line_anchor(hunk.filediff.source_file_path, old_line_no, 'o')
466 484 if new_line_no:
467 485 new_line_anchor = diff_line_anchor(hunk.filediff.target_file_path, new_line_no, 'n')
468 486 %>
469 487 <tr class="cb-line">
488 <td class="cb-data ${action_class(action)}">
489 <div>
490 %if comments:
491 <i class="icon-comment" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
492 %endif
493 </div>
494 </td>
470 495 <td class="cb-lineno ${action_class(action)}"
471 496 data-line-number="${old_line_no}"
472 497 %if old_line_anchor:
473 498 id="${old_line_anchor}"
474 499 %endif
475 500 >
476 501 %if old_line_anchor:
477 502 <a name="${old_line_anchor}" href="#${old_line_anchor}">${old_line_no}</a>
478 503 %endif
479 504 </td>
480 505 <td class="cb-lineno ${action_class(action)}"
481 506 data-line-number="${new_line_no}"
482 507 %if new_line_anchor:
483 508 id="${new_line_anchor}"
484 509 %endif
485 510 >
486 511 %if new_line_anchor:
487 512 <a name="${new_line_anchor}" href="#${new_line_anchor}">${new_line_no}</a>
488 513 %endif
489 514 </td>
490 515 <td class="cb-content ${action_class(action)}"
491 516 data-line-number="${new_line_no and 'n' or 'o'}${new_line_no or old_line_no}"
492 517 >
493 518 %if use_comments:
494 519 ${render_add_comment_button()}
495 520 %endif
496 521 <span class="cb-code">${action} ${content or '' | n}</span>
497 522 %if use_comments and comments:
498 523 ${inline_comments_container(comments)}
499 524 %endif
500 525 </td>
501 526 </tr>
502 527 %endfor
503 528 </%def>
504 529
505 530 <%def name="render_add_comment_button()">
506 531 <button
507 532 class="btn btn-small btn-primary cb-comment-box-opener"
508 533 onclick="return Rhodecode.comments.createComment(this)"
509 >+</button>
534 ><span>+</span></button>
510 535 </%def>
511 536
512 537 <%def name="render_diffset_menu()">
513 538 <div class="diffset-menu clearinner">
514 539 <div class="pull-right">
515 540 <div class="btn-group">
516 541 <a
517 542 class="btn ${c.diffmode == 'sideside' and 'btn-primary'} tooltip"
518 543 title="${_('View side by side')}"
519 544 href="${h.url_replace(diffmode='sideside')}">
520 545 <span>${_('Side by Side')}</span>
521 546 </a>
522 547 <a
523 548 class="btn ${c.diffmode == 'unified' and 'btn-primary'} tooltip"
524 549 title="${_('View unified')}" href="${h.url_replace(diffmode='unified')}">
525 550 <span>${_('Unified')}</span>
526 551 </a>
527 552 </div>
528 553 </div>
529 554 <div class="pull-left">
530 555 <div class="btn-group">
531 556 <a
532 557 class="btn"
533 558 href="#"
534 559 onclick="$('input[class=filediff-collapse-state]').prop('checked', false); return false">${_('Expand All')}</a>
535 560 <a
536 561 class="btn"
537 562 href="#"
538 563 onclick="$('input[class=filediff-collapse-state]').prop('checked', true); return false">${_('Collapse All')}</a>
539 564 </div>
540 565 </div>
541 566 </div>
542 567 </%def>
General Comments 0
You need to be logged in to leave comments. Login now