2010-08-29 Adam Barth <abarth@webkit.org>
[WebKit-https.git] / BugsSite / code-review.js
1 (function() {
2   var kDeleteImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAA3NCSVQICAjb4U/gAAAAn1BMVEX////4YmT/dnnyTE//dnn9bnH6am34YmT3XWD2WVv2VVjsOj3oMDLlJyrjICL2VVjzUVTwR0ruPT/iHB72WVvwR0rzUVT/h4r/gob/foH/eXv/dnn/cnT9bnH/bG76am3/Zmb6ZGf4YmT3XWD/WFv2WVv/VFf2VVj0TVDyTE/2SkzwR0rvREfuQUPuPT/sOj3rNDboMDLnLTDlJyrjICIhCpwnAAAANXRSTlMAESIiMzMzMzMzMzMzMzNERERERHd3qv///////////////////////////////////////0mgXpwAAAAJcEhZcwAAHngAAB54AcurAx8AAAAYdEVYdFNvZnR3YXJlAEFkb2JlIEZpcmV3b3Jrc0+zH04AAACVSURBVBiVbczXFoIwDAbguHGi4mqbWugQZInj/Z9NSuXAhblJvuTkB+jV4NeHY9e9g+/M2KSxFKdRY0JwWltxoo72gvRMxcxTgqrM/Qp2QWmdt+kRJ5SyzgCGao09zw3TN8yWnSNEfo3LVWdTPJIwqdbWCyN5XABUeZi+NvViG0trgHeRPgM77O6l+/04A+zb9AD+1Bf6lg3jQQJJTgAAAABJRU5ErkJggg==';
3
4   function determineAttachmentID() {
5     try {
6       return /id=(\d+)/.exec(window.location.search)[1]
7     } catch (ex) {
8       return;
9     }
10   }
11
12   // Attempt to activate only in the "Formatted Diff" context.
13   if (window.top != window)
14     return;
15   var attachment_id = determineAttachmentID();
16   if (!attachment_id)
17     return;
18
19   var previous_comments = [];
20   var current_comment = {}
21   var next_line_id = 0;
22
23   function idForLine(number) {
24     return 'line' + number;
25   }
26
27   function nextLineID() {
28     return idForLine(next_line_id++);
29   }
30
31   function forEachLine(callback) {
32     for (var i = 0; i < next_line_id; ++i) {
33       callback($('#' + idForLine(i)));
34     }
35   }
36
37   function idify() {
38     this.id = nextLineID();
39   }
40
41   function hoverify() {
42     $(this).hover(function() {
43       $(this).addClass('hot');
44     },
45     function () {
46       $(this).removeClass('hot');
47     });
48   }
49
50   function findCommentPositionFor(line) {
51     var position = line;
52     while (position.next() && position.next().hasClass('previous_comment'))
53       position = position.next();
54     return position;
55   }
56
57   function findCommentBlockFor(line) {
58     var comment_block = findCommentPositionFor(line).next();
59     if (!comment_block.hasClass('comment'))
60       return;
61     return comment_block;
62   }
63
64   function insertCommentFor(line, block) {
65     findCommentPositionFor(line).after(block);
66   }
67
68   function addCommentField() {
69     var id = $(this).attr('data-comment-for');
70     if (!id)
71       id = this.id;
72     var line = $('#' + id);
73     if (line.attr('data-has-comment'))
74       return;
75     line.attr('data-has-comment', 'true');
76     var comment_block = $('<div class="comment"><div class="actions"><img class="delete" src="' + kDeleteImage + '"></div><textarea data-comment-for="' + id + '"></textarea></div>');
77     insertCommentFor(line, comment_block);
78     comment_block.each(hoverify);
79     comment_block.children('textarea').focus();
80   }
81
82   function displayPreviousComments() {
83     comment_collection = this;
84     forEachLine(function(line) {
85       var line_id = line.attr('id');
86       var comment = comment_collection[line_id];
87       if (comment) {
88         var comment_block = $('<div data-comment-for="' + line_id + '" class="previous_comment"></div>');
89         comment_block.text(comment).prepend('<div class="reply">Click to reply</div>');
90         comment_block.each(hoverify).click(addCommentField);
91         insertCommentFor(line, comment_block);
92       }
93     });
94   }
95
96   $(document).ready(function() {
97     $('.Line').each(idify).each(hoverify).click(addCommentField);
98     $(previous_comments).each(displayPreviousComments);
99     $(document.body).prepend('<div id="toolbar"><button id="post_comments">Prepare comments</button></div>');
100     $(document.body).prepend('<div id="comment_form" class="inactive"><div class="winter"></div><div class="lightbox"><iframe src="attachment.cgi?id=' + attachment_id + '&action=reviewform"></iframe></div></div>');
101   });
102
103   $('.comment textarea').live('keydown', function() {
104     var line_id = $(this).attr('data-comment-for');
105     current_comment[line_id] = $(this).val();
106   });
107
108   $('.comment .delete').live('click', function() {
109     var line_id = $(this).parentsUntil('.comment').next().attr('data-comment-for');
110     delete current_comment[line_id];
111     var line = $('#' + line_id)
112     findCommentBlockFor(line).remove();
113     line.removeAttr('data-has-comment');
114   });
115
116   function contextSnippetFor(line) {
117     var file_name = line.parent().prev().text();
118     var line_number = line.hasClass('remove') ? line.children('.from').text() : line.children('.to').text();
119
120     var action = ' ';
121     if (line.hasClass('add'))
122       action = '+';
123     else if (line.hasClass('remove'))
124       action = '-';
125
126     var text = line.children('.text').text();
127     return '> ' + file_name + ':' + line_number + '\n> ' + action + text;
128   }
129
130   $('#comment_form .winter').live('click', function() {
131     $('#comment_form').addClass('inactive');
132   });
133
134   $('#post_comments').live('click', function() {
135     var comments_in_context = []
136     forEachLine(function(line) {
137       if (line.attr('data-has-comment') != 'true')
138         return;
139       var snippet = contextSnippetFor(line);
140       var comment = findCommentBlockFor(line).children('textarea').val();
141       if (comment == '')
142         return;
143       comments_in_context.push(snippet + '\n' + comment);
144     });
145     $('#comment_form').removeClass('inactive');
146     var comment = comments_in_context.join('\n\n');
147     console.log(comment);
148     $('#comment_form').find('iframe').contents().find('#comment').val(comment);
149   });
150 })();