1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131 |
- // Copyright (C) 2010 Adam Barth. All rights reserved.
- //
- // Redistribution and use in source and binary forms, with or without
- // modification, are permitted provided that the following conditions are met:
- //
- // 1. Redistributions of source code must retain the above copyright notice,
- // this list of conditions and the following disclaimer.
- //
- // 2. Redistributions in binary form must reproduce the above copyright notice,
- // this list of conditions and the following disclaimer in the documentation
- // and/or other materials provided with the distribution.
- //
- // THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS "AS IS" AND ANY
- // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- // DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
- // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
- // DAMAGE.
- var CODE_REVIEW_UNITTEST;
- (function() {
- /**
- * Create a new function with some of its arguements
- * pre-filled.
- * Taken from goog.partial in the Closure library.
- * @param {Function} fn A function to partially apply.
- * @param {...*} var_args Additional arguments that are partially
- * applied to fn.
- * @return {!Function} A partially-applied form of the function.
- */
- function partial(fn, var_args) {
- var args = Array.prototype.slice.call(arguments, 1);
- return function() {
- // Prepend the bound arguments to the current arguments.
- var newArgs = Array.prototype.slice.call(arguments);
- newArgs.unshift.apply(newArgs, args);
- return fn.apply(this, newArgs);
- };
- };
- function determineAttachmentID() {
- try {
- return /id=(\d+)/.exec(window.location.search)[1]
- } catch (ex) {
- return;
- }
- }
- // Attempt to activate only in the "Review Patch" context.
- if (window.top != window)
- return;
- if (!CODE_REVIEW_UNITTEST && !window.location.search.match(/action=review/)
- && !window.location.toString().match(/bugs\.webkit\.org\/PrettyPatch/))
- return;
- var attachment_id = determineAttachmentID();
- if (!attachment_id)
- console.log('No attachment ID');
- var minLeftSideRatio = 10;
- var maxLeftSideRatio = 90;
- var file_diff_being_resized = null;
- var files = {};
- var original_file_contents = {};
- var patched_file_contents = {};
- var WEBKIT_BASE_DIR = "//svn.webkit.org/repository/webkit/trunk/";
- var SIDE_BY_SIDE_DIFFS_KEY = 'sidebysidediffs';
- var g_displayed_draft_comments = false;
- var g_next_line_id = 0;
- var KEY_CODE = {
- down: 40,
- enter: 13,
- escape: 27,
- j: 74,
- k: 75,
- n: 78,
- p: 80,
- r: 82,
- up: 38
- }
- function idForLine(number) {
- return 'line' + number;
- }
- function forEachLine(callback) {
- var i = 0;
- for (var i = 0; i < g_next_line_id; i++) {
- var line = $('#' + idForLine(i));
- if (line[0])
- callback(line);
- }
- }
- function hoverify() {
- $(this).hover(function() {
- $(this).addClass('hot');
- },
- function () {
- $(this).removeClass('hot');
- });
- }
- function fileDiffFor(line) {
- return $(line).parents('.FileDiff');
- }
- function diffSectionFor(line) {
- return $(line).parents('.DiffSection');
- }
- function activeCommentFor(line) {
- // Scope to the diffSection as a performance improvement.
- return $('textarea[data-comment-for~="' + line[0].id + '"]', fileDiffFor(line));
- }
- function previousCommentsFor(line) {
- // Scope to the diffSection as a performance improvement.
- return $('div[data-comment-for~="' + line[0].id + '"].previousComment', fileDiffFor(line));
- }
- function findCommentPositionFor(line) {
- var previous_comments = previousCommentsFor(line);
- var num_previous_comments = previous_comments.size();
- if (num_previous_comments)
- return $(previous_comments[num_previous_comments - 1])
- return line;
- }
- function findCommentBlockFor(line) {
- var comment_block = findCommentPositionFor(line).next();
- if (!comment_block.hasClass('comment'))
- return;
- return comment_block;
- }
- function insertCommentFor(line, block) {
- findCommentPositionFor(line).after(block);
- }
- function addDraftComment(start_line_id, end_line_id, contents) {
- var line = $('#' + end_line_id);
- var start = numberFrom(start_line_id);
- var end = numberFrom(end_line_id);
- for (var i = start; i <= end; i++) {
- addDataCommentBaseLine($('#line' + i), end_line_id);
- }
- var comment_block = createCommentFor(line);
- $(comment_block).children('textarea').val(contents);
- freezeComment(comment_block);
- }
- function ensureDraftCommentsDisplayed() {
- if (g_displayed_draft_comments)
- return;
- g_displayed_draft_comments = true;
- var comments = g_draftCommentSaver.saved_comments();
- var errors = [];
- $(comments.comments).each(function() {
- try {
- addDraftComment(this.start_line_id, this.end_line_id, this.contents);
- } catch (e) {
- errors.push({'start': this.start_line_id, 'end': this.end_line_id, 'contents': this.contents});
- }
- });
-
- if (errors.length) {
- console.log('DRAFT COMMENTS WITH ERRORS:', JSON.stringify(errors));
- alert('Some draft comments failed to be added. See the console to manually resolve.');
- }
-
- var overall_comments = comments['overall-comments'];
- if (overall_comments) {
- openOverallComments();
- $('.overallComments textarea').val(overall_comments);
- }
- }
- function DraftCommentSaver(opt_attachment_id, opt_localStorage) {
- this._attachment_id = opt_attachment_id || attachment_id;
- this._localStorage = opt_localStorage || localStorage;
- this._save_comments = true;
- }
- DraftCommentSaver.prototype._json = function() {
- var comments = $('.comment');
- var comment_store = [];
- comments.each(function () {
- var file_diff = fileDiffFor(this);
- var textarea = $('textarea', this);
- var contents = textarea.val().trim();
- if (!contents)
- return;
- var comment_base_line = textarea.attr('data-comment-for');
- var lines = contextLinesFor(comment_base_line, file_diff);
- comment_store.push({
- start_line_id: lines.first().attr('id'),
- end_line_id: comment_base_line,
- contents: contents
- });
- });
- var overall_comments = $('.overallComments textarea').val().trim();
- return JSON.stringify({'born-on': Date.now(), 'comments': comment_store, 'overall-comments': overall_comments});
- }
-
- DraftCommentSaver.prototype.localStorageKey = function() {
- return DraftCommentSaver._keyPrefix + this._attachment_id;
- }
-
- DraftCommentSaver.prototype.saved_comments = function() {
- var serialized_comments = this._localStorage.getItem(this.localStorageKey());
- if (!serialized_comments)
- return [];
- var comments = {};
- try {
- comments = JSON.parse(serialized_comments);
- } catch (e) {
- this._erase_corrupt_comments();
- return {};
- }
- var individual_comments = comments.comments;
- if (!comments || !comments['born-on'] || !individual_comments || (individual_comments.length && !individual_comments[0].contents)) {
- this._erase_corrupt_comments();
- return {};
- }
- return comments;
- }
-
- DraftCommentSaver.prototype._erase_corrupt_comments = function() {
- // FIXME: Show an error to the user instead of logging.
- console.log('Draft comments were corrupted. Erasing comments.');
- this.erase();
- }
-
- DraftCommentSaver.prototype.save = function() {
- if (!this._save_comments)
- return;
- var key = this.localStorageKey();
- var value = this._json();
- if (this._attemptToWrite(key, value))
- return;
- this._eraseOldCommentsForAllReviews();
- if (this._attemptToWrite(key, value))
- return;
- var remove_comments = this._should_remove_comments();
- if (!remove_comments) {
- this._save_comments = false;
- return;
- }
- this._eraseCommentsForAllReviews();
- if (this._attemptToWrite(key, value))
- return;
- this._save_comments = false;
- // FIXME: Show an error to the user.
- }
- DraftCommentSaver.prototype._should_remove_comments = function(message) {
- return prompt('Local storage quota is full. Remove draft comments from all previous reviews to make room?');
- }
- DraftCommentSaver.prototype._attemptToWrite = function(key, value) {
- try {
- this._localStorage.setItem(key, value);
- return true;
- } catch (e) {
- return false;
- }
- }
- DraftCommentSaver._keyPrefix = 'draft-comments-for-attachment-';
- DraftCommentSaver.prototype.erase = function() {
- this._localStorage.removeItem(this.localStorageKey());
- }
- DraftCommentSaver.prototype._eraseOldCommentsForAllReviews = function() {
- this._eraseComments(true);
- }
- DraftCommentSaver.prototype._eraseCommentsForAllReviews = function() {
- this._eraseComments(false);
- }
- var MONTH_IN_MS = 1000 * 60 * 60 * 24 * 30;
- DraftCommentSaver.prototype._eraseComments = function(only_old_reviews) {
- var length = this._localStorage.length;
- var keys_to_delete = [];
- for (var i = 0; i < length; i++) {
- var key = this._localStorage.key(i);
- if (key.indexOf(DraftCommentSaver._keyPrefix) != 0)
- continue;
-
- if (only_old_reviews) {
- try {
- var born_on = JSON.parse(this._localStorage.getItem(key))['born-on'];
- if (Date.now() - born_on < MONTH_IN_MS)
- continue;
- } catch (e) {
- console.log('Deleting JSON. JSON for code review is corrupt: ' + key);
- }
- }
- keys_to_delete.push(key);
- }
- for (var i = 0; i < keys_to_delete.length; i++) {
- this._localStorage.removeItem(keys_to_delete[i]);
- }
- }
-
- var g_draftCommentSaver = new DraftCommentSaver();
- function saveDraftComments() {
- ensureDraftCommentsDisplayed();
- g_draftCommentSaver.save();
- setAutoSaveStateIndicator('saved');
- }
- function setAutoSaveStateIndicator(state) {
- var container = $('.autosave-state');
- container.text(state);
-
- if (state == 'saving')
- container.addClass(state);
- else
- container.removeClass('saving');
- }
-
- function unfreezeCommentFor(line) {
- // FIXME: This query is overly complex because we place comment blocks
- // after Lines. Instead, comment blocks should be children of Lines.
- findCommentPositionFor(line).next().next().filter('.frozenComment').each(handleUnfreezeComment);
- }
- function createCommentFor(line) {
- if (line.attr('data-has-comment')) {
- unfreezeCommentFor(line);
- return;
- }
- line.attr('data-has-comment', 'true');
- line.addClass('commentContext');
- var comment_block = $('<div class="comment"><textarea data-comment-for="' + line.attr('id') + '"></textarea><div class="actions"><button class="ok">OK</button><button class="discard">Discard</button></div></div>');
- $('textarea', comment_block).bind('input', handleOverallCommentsInput);
- insertCommentFor(line, comment_block);
- return comment_block;
- }
- function addCommentFor(line) {
- var comment_block = createCommentFor(line);
- if (!comment_block)
- return;
- comment_block.hide().slideDown('fast', function() {
- $(this).children('textarea').focus();
- });
- return comment_block;
- }
- function addCommentField(comment_block) {
- var id = $(comment_block).attr('data-comment-for');
- if (!id)
- id = comment_block.id;
- return addCommentFor($('#' + id));
- }
-
- function handleAddCommentField() {
- addCommentField(this);
- }
- function addPreviousComment(line, author, comment_text) {
- var line_id = $(line).attr('id');
- var comment_block = $('<div data-comment-for="' + line_id + '" class="previousComment"></div>');
- var author_block = $('<div class="author"></div>').text(author + ':');
- var text_block = $('<div class="content"></div>').text(comment_text);
- comment_block.append(author_block).append(text_block).each(hoverify).click(handleAddCommentField);
- addDataCommentBaseLine($(line), line_id);
- insertCommentFor($(line), comment_block);
- }
- function displayPreviousComments(comments) {
- for (var i = 0; i < comments.length; ++i) {
- var author = comments[i].author;
- var file_name = comments[i].file_name;
- var line_number = comments[i].line_number;
- var comment_text = comments[i].comment_text;
- var file = files[file_name];
- var query = '.Line .to';
- if (line_number[0] == '-') {
- // The line_number represent a removal. We need to adjust the query to
- // look at the "from" lines.
- query = '.Line .from';
- // Trim off the '-' control character.
- line_number = line_number.substr(1);
- }
- $(file).find(query).each(function() {
- if ($(this).text() != line_number)
- return;
- var line = lineContainerFromDescendant($(this));
- addPreviousComment(line, author, comment_text);
- });
- }
- if (comments.length == 0) {
- return;
- }
- descriptor = comments.length + ' comment';
- if (comments.length > 1)
- descriptor += 's';
- $('.help .more').before(' This patch has ' + descriptor + '. Scroll through them with the "n" and "p" keys. ');
- }
-
- function showMoreHelp() {
- $('.more-help').removeClass('inactive');
- }
-
- function hideMoreHelp() {
- $('.more-help').addClass('inactive');
- }
- function scanForStyleQueueComments(text) {
- var comments = []
- var lines = text.split('\n');
- for (var i = 0; i < lines.length; ++i) {
- var parts = lines[i].match(/^([^:]+):(-?\d+):(.*)$/);
- if (!parts)
- continue;
- var file_name = parts[1];
- var line_number = parts[2];
- var comment_text = parts[3].trim();
- if (!file_name in files) {
- console.log('Filename in style queue output is not in the patch: ' + file_name);
- continue;
- }
- comments.push({
- 'author': 'StyleQueue',
- 'file_name': file_name,
- 'line_number': line_number,
- 'comment_text': comment_text
- });
- }
- return comments;
- }
- function scanForComments(author, text) {
- var comments = []
- var lines = text.split('\n');
- for (var i = 0; i < lines.length; ++i) {
- var parts = lines[i].match(/^([> ]+)([^:]+):(-?\d+)$/);
- if (!parts)
- continue;
- var quote_markers = parts[1];
- var file_name = parts[2];
- // FIXME: Store multiple lines for multiline comments and correctly import them here.
- var line_number = parts[3];
- if (!file_name in files)
- continue;
- while (i < lines.length && lines[i].length > 0 && lines[i][0] == '>')
- ++i;
- var comment_lines = [];
- while (i < lines.length && (lines[i].length == 0 || lines[i][0] != '>')) {
- comment_lines.push(lines[i]);
- ++i;
- }
- --i; // Decrement i because the for loop will increment it again in a second.
- var comment_text = comment_lines.join('\n').trim();
- comments.push({
- 'author': author,
- 'file_name': file_name,
- 'line_number': line_number,
- 'comment_text': comment_text
- });
- }
- return comments;
- }
- function isReviewFlag(select) {
- return $(select).attr('title') == 'Request for patch review.';
- }
- function isCommitQueueFlag(select) {
- return $(select).attr('title').match(/commit-queue/);
- }
- function findControlForFlag(select) {
- if (isReviewFlag(select))
- return $('#toolbar .review select');
- else if (isCommitQueueFlag(select))
- return $('#toolbar .commitQueue select');
- return $();
- }
- function addFlagsForAttachment(details) {
- var flag_control = "<select><option></option><option>?</option><option>+</option><option>-</option></select>";
- $('#flagContainer').append(
- $('<span class="review"> r: ' + flag_control + '</span>')).append(
- $('<span class="commitQueue"> cq: ' + flag_control + '</span>'));
- details.find('#flags select').each(function() {
- var requestee = $(this).parent().siblings('td:first-child').text().trim();
- if (requestee.length) {
- // Remove trailing ':'.
- requestee = requestee.substr(0, requestee.length - 1);
- requestee = ' (' + requestee + ')';
- }
- var control = findControlForFlag(this)
- control.attr('selectedIndex', $(this).attr('selectedIndex'));
- control.parent().prepend(requestee);
- });
- }
- window.addEventListener('message', function(e) {
- if (e.origin != 'https://webkit-queues.appspot.com')
- return;
- if (e.data.height) {
- $('.statusBubble')[0].style.height = e.data.height;
- $('.statusBubble')[0].style.width = e.data.width;
- }
- }, false);
- function handleStatusBubbleLoad(e) {
- e.target.contentWindow.postMessage('containerMetrics', 'https://webkit-queues.appspot.com');
- }
- function fetchHistory() {
- $.get('attachment.cgi?id=' + attachment_id + '&action=edit', function(data) {
- var bug_id = /Attachment \d+ Details for Bug (\d+)/.exec(data)[1];
- $.get('show_bug.cgi?id=' + bug_id, function(data) {
- var comments = [];
- $(data).find('.bz_comment').each(function() {
- var author = $(this).find('.email').text();
- var text = $(this).find('.bz_comment_text').text();
- var comment_marker = '(From update of attachment ' + attachment_id + ' .details.)';
- if (text.match(comment_marker))
- $.merge(comments, scanForComments(author, text));
- var style_queue_comment_marker = 'Attachment ' + attachment_id + ' .details. did not pass style-queue.'
- if (text.match(style_queue_comment_marker))
- $.merge(comments, scanForStyleQueueComments(text));
- });
- displayPreviousComments(comments);
- ensureDraftCommentsDisplayed();
- });
- var details = $(data);
- addFlagsForAttachment(details);
- statusBubble = document.createElement('iframe');
- statusBubble.className = 'statusBubble';
- statusBubble.src = 'https://webkit-queues.appspot.com/status-bubble/' + attachment_id;
- statusBubble.scrolling = 'no';
- // Can't append the HTML because we need to set the onload handler before appending the iframe to the DOM.
- statusBubble.onload = handleStatusBubbleLoad;
- $('#statusBubbleContainer').append(statusBubble);
- $('#toolbar .bugLink').html('<a href="/show_bug.cgi?id=' + bug_id + '" target="_blank">Bug ' + bug_id + '</a>');
- });
- }
- function firstLine(file_diff) {
- var container = $('.LineContainer:not(.context)', file_diff)[0];
- if (!container)
- return 0;
- var from = fromLineNumber(container);
- var to = toLineNumber(container);
- return from || to;
- }
- function crawlDiff() {
- g_next_line_id = 0;
- var idify = function() {
- this.id = idForLine(g_next_line_id++);
- }
- $('.Line').each(idify).each(hoverify);
- $('.FileDiff').each(function() {
- var header = $(this).children('h1');
- var url_hash = '#L' + firstLine(this);
- var file_name = header.text();
- files[file_name] = this;
- addExpandLinks(file_name);
- var diff_links = $('<div class="FileDiffLinkContainer LinkContainer">' +
- diffLinksHtml() +
- '</div>');
- var file_link = $('a', header)[0];
- // If the base directory in the file path does not match a WebKit top level directory,
- // then PrettyPatch.rb doesn't linkify the header.
- if (file_link) {
- file_link.target = "_blank";
- file_link.href += url_hash;
- diff_links.append(tracLinks(file_name, url_hash));
- }
- $('h1', this).after(diff_links);
- updateDiffLinkVisibility(this);
- });
- }
- function tracLinks(file_name, url_hash) {
- var trac_links = $('<a target="_blank">annotate</a><a target="_blank">revision log</a>');
- trac_links[0].href = 'http://trac.webkit.org/browser/trunk/' + file_name + '?annotate=blame' + url_hash;
- trac_links[1].href = 'http://trac.webkit.org/log/trunk/' + file_name;
- var implementation_suffix_list = ['.cpp', '.mm'];
- for (var i = 0; i < implementation_suffix_list.length; ++i) {
- var suffix = implementation_suffix_list[i];
- if (file_name.lastIndexOf(suffix) == file_name.length - suffix.length) {
- var new_link = $('<a target="_blank">header</a>');
- var stem = file_name.substr(0, file_name.length - suffix.length);
- new_link[0].href= 'http://trac.webkit.org/log/trunk/' + stem + '.h';
- trac_links = $.merge(new_link, trac_links);
- }
- }
- return trac_links;
- }
- function isChangeLog(file_name) {
- return file_name.match(/\/ChangeLog$/) || file_name == 'ChangeLog';
- }
- function addExpandLinks(file_name) {
- if (isChangeLog(file_name))
- return;
- var file_diff = files[file_name];
- // Don't show the links to expand upwards/downwards if the patch starts/ends without context
- // lines, i.e. starts/ends with add/remove lines.
- var first_line = file_diff.querySelector('.LineContainer:not(.context)');
- // If there is no element with a "Line" class, then this is an image diff.
- if (!first_line)
- return;
- var expand_bar_index = 0;
- if (!$(first_line).hasClass('add') && !$(first_line).hasClass('remove'))
- $('h1', file_diff).after(expandBarHtml(BELOW))
- $('br', file_diff).replaceWith(expandBarHtml());
- // jquery doesn't support :last-of-type, so use querySelector instead.
- var last_line = file_diff.querySelector('.LineContainer:last-of-type');
- // Some patches for new files somehow end up with an empty context line at the end
- // with a from line number of 0. Don't show expand links in that case either.
- if (!$(last_line).hasClass('add') && !$(last_line).hasClass('remove') && fromLineNumber(last_line) != 0)
- $(file_diff.querySelector('.DiffSection:last-of-type')).after(expandBarHtml(ABOVE));
- }
- function expandBarHtml(opt_direction) {
- var html = '<div class="ExpandBar">' +
- '<div class="ExpandArea Expand' + ABOVE + '"></div>' +
- '<div class="ExpandLinkContainer LinkContainer"><span class="ExpandText">expand: </span>';
- // FIXME: If there are <100 line to expand, don't show the expand-100 link.
- // If there are <20 lines to expand, don't show the expand-20 link.
- if (!opt_direction || opt_direction == ABOVE) {
- html += expandLinkHtml(ABOVE, 100) +
- expandLinkHtml(ABOVE, 20);
- }
- html += expandLinkHtml(ALL);
- if (!opt_direction || opt_direction == BELOW) {
- html += expandLinkHtml(BELOW, 20) +
- expandLinkHtml(BELOW, 100);
- }
- html += '</div><div class="ExpandArea Expand' + BELOW + '"></div></div>';
- return html;
- }
- function expandLinkHtml(direction, amount) {
- return "<a class='ExpandLink' href='javascript:' data-direction='" + direction + "' data-amount='" + amount + "'>" +
- (amount ? amount + " " : "") + direction + "</a>";
- }
- function handleExpandLinkClick() {
- var expand_bar = $(this).parents('.ExpandBar');
- var file_name = expand_bar.parents('.FileDiff').children('h1')[0].textContent;
- var expand_function = partial(expand, expand_bar[0], file_name, this.getAttribute('data-direction'), Number(this.getAttribute('data-amount')));
- if (file_name in original_file_contents)
- expand_function();
- else
- getWebKitSourceFile(file_name, expand_function, expand_bar);
- }
- function handleSideBySideLinkClick() {
- convertDiff('sidebyside', this);
- }
- function handleUnifyLinkClick() {
- convertDiff('unified', this);
- }
- function convertDiff(difftype, convert_link) {
- var file_diffs = $(convert_link).parents('.FileDiff');
- if (!file_diffs.size()) {
- localStorage.setItem('code-review-diffstate', difftype);
- file_diffs = $('.FileDiff');
- }
- convertAllFileDiffs(difftype, file_diffs);
- }
- function patchRevision() {
- var revision = $('.revision');
- return revision[0] ? revision.first().text() : null;
- }
- function setFileContents(file_name, original_contents, patched_contents) {
- original_file_contents[file_name] = original_contents;
- patched_file_contents[file_name] = patched_contents;
- }
- function getWebKitSourceFile(file_name, onLoad, expand_bar) {
- function handleLoad(contents) {
- var split_contents = contents.split('\n');
- setFileContents(file_name, split_contents, applyDiff(split_contents, file_name));
- onLoad();
- };
- var revision = patchRevision();
- var queryParameters = revision ? '?p=' + revision : '';
- $.ajax({
- url: WEBKIT_BASE_DIR + file_name + queryParameters,
- context: document.body,
- complete: function(xhr, data) {
- if (xhr.status == 0)
- handleLoadError(expand_bar);
- else
- handleLoad(xhr.responseText);
- }
- });
- }
- function replaceExpandLinkContainers(expand_bar, text) {
- $('.ExpandLinkContainer', $(expand_bar).parents('.FileDiff')).replaceWith('<span class="ExpandText">' + text + '</span>');
- }
- function handleLoadError(expand_bar) {
- replaceExpandLinkContainers(expand_bar, "Can't expand. Is this a new or deleted file?");
- }
- var ABOVE = 'above';
- var BELOW = 'below';
- var ALL = 'all';
- function lineNumbersFromSet(set, is_last) {
- var to = -1;
- var from = -1;
- var size = set.size();
- var start = is_last ? (size - 1) : 0;
- var end = is_last ? -1 : size;
- var offset = is_last ? -1 : 1;
- for (var i = start; i != end; i += offset) {
- if (to != -1 && from != -1)
- return {to: to, from: from};
- var line_number = set[i];
- if ($(line_number).hasClass('to')) {
- if (to == -1)
- to = Number(line_number.textContent);
- } else {
- if (from == -1)
- from = Number(line_number.textContent);
- }
- }
- }
- function removeContextBarBelow(expand_bar) {
- $('.context', expand_bar.nextElementSibling).detach();
- }
- function expand(expand_bar, file_name, direction, amount) {
- if (file_name in original_file_contents && !patched_file_contents[file_name]) {
- // FIXME: In this case, try fetching the source file at the revision the patch was created at.
- // Might need to modify webkit-patch to include that data in the diff.
- replaceExpandLinkContainers(expand_bar, "Can't expand. Unable to apply patch to tip of tree.");
- return;
- }
- var above_expansion = expand_bar.querySelector('.Expand' + ABOVE)
- var below_expansion = expand_bar.querySelector('.Expand' + BELOW)
- var above_line_numbers = $('.expansionLineNumber', above_expansion);
- if (!above_line_numbers[0]) {
- var diff_section = expand_bar.previousElementSibling;
- above_line_numbers = $('.Line:not(.context) .lineNumber', diff_section);
- }
- var above_last_line_num, above_last_from_line_num;
- if (above_line_numbers[0]) {
- var above_numbers = lineNumbersFromSet(above_line_numbers, true);
- above_last_line_num = above_numbers.to;
- above_last_from_line_num = above_numbers.from;
- } else
- above_last_from_line_num = above_last_line_num = 0;
- var below_line_numbers = $('.expansionLineNumber', below_expansion);
- if (!below_line_numbers[0]) {
- var diff_section = expand_bar.nextElementSibling;
- if (diff_section)
- below_line_numbers = $('.Line:not(.context) .lineNumber', diff_section);
- }
- var below_first_line_num, below_first_from_line_num;
- if (below_line_numbers[0]) {
- var below_numbers = lineNumbersFromSet(below_line_numbers, false);
- below_first_line_num = below_numbers.to - 1;
- below_first_from_line_num = below_numbers.from - 1;
- } else
- below_first_from_line_num = below_first_line_num = patched_file_contents[file_name].length - 1;
- var start_line_num, start_from_line_num;
- var end_line_num;
- if (direction == ABOVE) {
- start_from_line_num = above_last_from_line_num;
- start_line_num = above_last_line_num;
- end_line_num = Math.min(start_line_num + amount, below_first_line_num);
- } else if (direction == BELOW) {
- end_line_num = below_first_line_num;
- start_line_num = Math.max(end_line_num - amount, above_last_line_num)
- start_from_line_num = Math.max(below_first_from_line_num - amount, above_last_from_line_num)
- } else { // direction == ALL
- start_line_num = above_last_line_num;
- start_from_line_num = above_last_from_line_num;
- end_line_num = below_first_line_num;
- }
- var lines = expansionLines(file_name, expansion_area, direction, start_line_num, end_line_num, start_from_line_num);
- var expansion_area;
- // Filling in all the remaining lines. Overwrite the expand links.
- if (start_line_num == above_last_line_num && end_line_num == below_first_line_num) {
- $('.ExpandLinkContainer', expand_bar).detach();
- below_expansion.insertBefore(lines, below_expansion.firstChild);
- removeContextBarBelow(expand_bar);
- } else if (direction == ABOVE) {
- above_expansion.appendChild(lines);
- } else {
- below_expansion.insertBefore(lines, below_expansion.firstChild);
- removeContextBarBelow(expand_bar);
- }
- }
- function unifiedLine(from, to, contents, is_expansion_line, opt_className, opt_attributes) {
- var className = is_expansion_line ? 'ExpansionLine' : 'LineContainer Line';
- if (opt_className)
- className += ' ' + opt_className;
- var lineNumberClassName = is_expansion_line ? 'expansionLineNumber' : 'lineNumber';
- var line = $('<div class="' + className + '" ' + (opt_attributes || '') + '>' +
- '<span class="from ' + lineNumberClassName + '">' + (from || ' ') +
- '</span><span class="to ' + lineNumberClassName + '">' + (to || ' ') +
- '</span><span class="text"></span>' +
- '</div>');
- $('.text', line).replaceWith(contents);
- return line;
- }
- function unifiedExpansionLine(from, to, contents) {
- return unifiedLine(from, to, contents, true);
- }
- function sideBySideExpansionLine(from, to, contents) {
- var line = $('<div class="ExpansionLine"></div>');
- // Clone the contents so we have two copies we can put back in the DOM.
- line.append(lineSide('from', contents.clone(true), true, from));
- line.append(lineSide('to', contents, true, to));
- return line;
- }
- function lineSide(side, contents, is_expansion_line, opt_line_number, opt_attributes, opt_class) {
- var class_name = '';
- if (opt_attributes || opt_class) {
- class_name = 'class="';
- if (opt_attributes)
- class_name += is_expansion_line ? 'ExpansionLine' : 'Line';
- class_name += ' ' + (opt_class || '') + '"';
- }
- var attributes = opt_attributes || '';
- var line_side = $('<div class="LineSide">' +
- '<div ' + attributes + ' ' + class_name + '>' +
- '<span class="' + side + ' ' + (is_expansion_line ? 'expansionLineNumber' : 'lineNumber') + '">' +
- (opt_line_number || ' ') +
- '</span>' +
- '<span class="text"></span>' +
- '</div>' +
- '</div>');
- $('.text', line_side).replaceWith(contents);
- return line_side;
- }
- function expansionLines(file_name, expansion_area, direction, start_line_num, end_line_num, start_from_line_num) {
- var fragment = document.createDocumentFragment();
- var is_side_by_side = isDiffSideBySide(files[file_name]);
- for (var i = 0; i < end_line_num - start_line_num; i++) {
- var from = start_from_line_num + i + 1;
- var to = start_line_num + i + 1;
- var contents = $('<span class="text"></span>');
- contents.text(patched_file_contents[file_name][start_line_num + i]);
- var line = is_side_by_side ? sideBySideExpansionLine(from, to, contents) : unifiedExpansionLine(from, to, contents);
- fragment.appendChild(line[0]);
- }
- return fragment;
- }
- function hunkStartingLine(patched_file, context, prev_line, hunk_num) {
- var current_line = -1;
- var last_context_line = context[context.length - 1];
- if (patched_file[prev_line] == last_context_line)
- current_line = prev_line + 1;
- else {
- console.log('Hunk #' + hunk_num + ' FAILED.');
- return -1;
- }
- // For paranoia sake, confirm the rest of the context matches;
- for (var i = 0; i < context.length - 1; i++) {
- if (patched_file[current_line - context.length + i] != context[i]) {
- console.log('Hunk #' + hunk_num + ' FAILED. Did not match preceding context.');
- return -1;
- }
- }
- return current_line;
- }
- function fromLineNumber(line) {
- var node = line.querySelector('.from');
- return node ? Number(node.textContent) : 0;
- }
- function toLineNumber(line) {
- var node = line.querySelector('.to');
- return node ? Number(node.textContent) : 0;
- }
- function textContentsFor(line) {
- // Just get the first match since a side-by-side diff has two lines with text inside them for
- // unmodified lines in the diff.
- return $('.text', line).first().text();
- }
- function lineNumberForFirstNonContextLine(patched_file, line, prev_line, context, hunk_num) {
- if (context.length) {
- var prev_line_num = fromLineNumber(prev_line) - 1;
- return hunkStartingLine(patched_file, context, prev_line_num, hunk_num);
- }
- if (toLineNumber(line) == 1 || fromLineNumber(line) == 1)
- return 0;
- console.log('Failed to apply patch. Adds or removes lines before any context lines.');
- return -1;
- }
- function applyDiff(original_file, file_name) {
- var diff_sections = files[file_name].getElementsByClassName('DiffSection');
- var patched_file = original_file.concat([]);
- // Apply diffs in reverse order to avoid needing to keep track of changing line numbers.
- for (var i = diff_sections.length - 1; i >= 0; i--) {
- var section = diff_sections[i];
- var lines = $('.Line:not(.context)', section);
- var current_line = -1;
- var context = [];
- var hunk_num = i + 1;
- for (var j = 0, lines_len = lines.length; j < lines_len; j++) {
- var line = lines[j];
- var line_contents = textContentsFor(line);
- if ($(line).hasClass('add')) {
- if (current_line == -1) {
- current_line = lineNumberForFirstNonContextLine(patched_file, line, lines[j-1], context, hunk_num);
- if (current_line == -1)
- return null;
- }
- patched_file.splice(current_line, 0, line_contents);
- current_line++;
- } else if ($(line).hasClass('remove')) {
- if (current_line == -1) {
- current_line = lineNumberForFirstNonContextLine(patched_file, line, lines[j-1], context, hunk_num);
- if (current_line == -1)
- return null;
- }
- if (patched_file[current_line] != line_contents) {
- console.log('Hunk #' + hunk_num + ' FAILED.');
- return null;
- }
- patched_file.splice(current_line, 1);
- } else if (current_line == -1) {
- context.push(line_contents);
- } else if (line_contents != patched_file[current_line]) {
- console.log('Hunk #' + hunk_num + ' FAILED. Context at end did not match');
- return null;
- } else {
- current_line++;
- }
- }
- }
- return patched_file;
- }
- function openOverallComments(e) {
- $('.overallComments textarea').addClass('open');
- $('#statusBubbleContainer').addClass('wrap');
- }
- var g_overallCommentsInputTimer;
- function handleOverallCommentsInput() {
- setAutoSaveStateIndicator('saving');
- // Save draft comments after we haven't received an input event in 1 second.
- if (g_overallCommentsInputTimer)
- clearTimeout(g_overallCommentsInputTimer);
- g_overallCommentsInputTimer = setTimeout(saveDraftComments, 1000);
- }
- function diffLinksHtml() {
- return '<a href="javascript:" class="unify-link">unified</a>' +
- '<a href="javascript:" class="side-by-side-link">side-by-side</a>';
- }
- function appendToolbar() {
- $(document.body).append('<div id="toolbar">' +
- '<div class="overallComments">' +
- '<textarea placeholder="Overall comments"></textarea>' +
- '</div>' +
- '<div>' +
- '<span id="statusBubbleContainer"></span>' +
- '<span class="actions">' +
- '<span class="links"><span class="bugLink"></span></span>' +
- '<span id="flagContainer"></span>' +
- '<button id="preview_comments">Preview</button>' +
- '<button id="post_comments">Publish</button> ' +
- '</span>' +
- '<div class="clear_float"></div>' +
- '</div>' +
- '<div class="autosave-state"></div>' +
- '</div>');
- $('.overallComments textarea').bind('click', openOverallComments);
- $('.overallComments textarea').bind('input', handleOverallCommentsInput);
- var toolbar = $('#toolbar');
- toolbar.css('position', '-webkit-sticky');
- var supportsSticky = toolbar.css('position') == '-webkit-sticky';
- document.body.style.marginBottom = supportsSticky ? 0 : '40px';
- }
- function handleDocumentReady() {
- crawlDiff();
- fetchHistory();
- $(document.body).prepend('<div id="message">' +
- '<div class="help">Select line numbers to add a comment. Scroll though diffs with the "j" and "k" keys.' +
- '<div class="DiffLinks LinkContainer">' +
- '<input type="checkbox" id="line-number-on-copy"><label for="line-number-on-copy">Skip line numbers on copy</label>' +
- diffLinksHtml() +
- '</div>' +
- '<a href="javascript:" class="more">[more]</a>' +
- '<div class="more-help inactive">' +
- '<div class="winter"></div>' +
- '<div class="lightbox"><table>' +
- '<tr><td>enter</td><td>add/edit comment for focused item</td></tr>' +
- '<tr><td>escape</td><td>accept current comment / close preview and help popups</td></tr>' +
- '<tr><td>j</td><td>focus next diff</td></tr>' +
- '<tr><td>k</td><td>focus previous diff</td></tr>' +
- '<tr><td>shift + j</td><td>focus next line</td></tr>' +
- '<tr><td>shift + k</td><td>focus previous line</td></tr>' +
- '<tr><td>n</td><td>focus next comment</td></tr>' +
- '<tr><td>p</td><td>focus previous comment</td></tr>' +
- '<tr><td>r</td><td>focus review select element</td></tr>' +
- '<tr><td>ctrl + shift + up</td><td>extend context of the focused comment</td></tr>' +
- '<tr><td>ctrl + shift + down</td><td>shrink context of the focused comment</td></tr>' +
- '</table></div>' +
- '</div>' +
- '</div>' +
- '</div>');
- appendToolbar();
- $(document.body).prepend('<div id="comment_form" class="inactive"><div class="winter"></div><div class="lightbox"><iframe id="reviewform" src="attachment.cgi?id=' + attachment_id + '&action=reviewform"></iframe></div></div>');
- $('#reviewform').bind('load', handleReviewFormLoad);
- loadDiffState();
- generateFileDiffResizeStyleElement();
- updateLineNumberOnCopyLinkContents();
- document.body.addEventListener('copy', handleCopy);
- };
- function forEachNode(nodeList, callback) {
- Array.prototype.forEach.call(nodeList, callback);
- }
- $('#line-number-on-copy').live('click', toggleShouldStripLineNumbersOnCopy);
- function updateLineNumberOnCopyLinkContents() {
- document.getElementById('line-number-on-copy').checked = shouldStripLineNumbersOnCopy();
- }
- function shouldStripLineNumbersOnCopy() {
- return localStorage.getItem('code-review-line-numbers-on-copy') == 'true';
- }
- function toggleShouldStripLineNumbersOnCopy() {
- localStorage.setItem('code-review-line-numbers-on-copy', !shouldStripLineNumbersOnCopy());
- updateLineNumberOnCopyLinkContents();
- }
- function sanitizeFragmentForCopy(fragment, shouldStripLineNumbers) {
- var classesToRemove = ['LinkContainer'];
- if (shouldStripLineNumbers)
- classesToRemove.push('lineNumber');
- classesToRemove.forEach(function(className) {
- forEachNode(fragment.querySelectorAll('.' + className), function(node) {
- $(node).remove();
- });
- });
- // Ensure that empty newlines show up in the copy now that
- // the line might collapse since the line number doesn't take up space.
- forEachNode(fragment.querySelectorAll('.text'), function(node) {
- if (node.textContent.match(/^\s*$/))
- node.innerHTML = '<br>';
- });
- }
- function handleCopy(event) {
- if (event.target.tagName == 'TEXTAREA')
- return;
- var selection = window.getSelection();
- var range = selection.getRangeAt(0);
- var selectionFragment = range.cloneContents();
- sanitizeFragmentForCopy(selectionFragment, shouldStripLineNumbersOnCopy())
- // FIXME: When event.clipboardData.setData supports text/html, remove all the code below.
- // https://bugs.webkit.org/show_bug.cgi?id=104179
- var container = document.createElement('div');
- container.appendChild(selectionFragment);
- document.body.appendChild(container);
- selection.selectAllChildren(container);
- setTimeout(function() {
- $(container).remove();
- selection.removeAllRanges();
- selection.addRange(range);
- });
- }
- function handleReviewFormLoad() {
- var review_form_contents = $('#reviewform').contents();
- if (review_form_contents[0].querySelector('#form-controls #flags')) {
- review_form_contents.bind('keydown', function(e) {
- if (e.keyCode == KEY_CODE.escape)
- hideCommentForm();
- });
- // This is the intial load of the review form iframe.
- var form = review_form_contents.find('form')[0];
- form.addEventListener('submit', eraseDraftComments);
- form.target = '';
- return;
- }
- // Review form iframe have the publish button has been pressed.
- var email_sent_to = review_form_contents[0].querySelector('#bugzilla-body dl');
- // If the email_send_to DL is not in the tree that means the publish failed for some reason,
- // e.g., you're not logged in. Show the comment form to allow you to login.
- if (!email_sent_to) {
- showCommentForm();
- return;
- }
- eraseDraftComments();
- // FIXME: Once WebKit supports seamless iframes, we can just make the review-form
- // iframe fill the page instead of redirecting back to the bug.
- window.location.replace($('#toolbar .bugLink a').attr('href'));
- }
-
- function eraseDraftComments() {
- g_draftCommentSaver.erase();
- }
- function loadDiffState() {
- var diffstate = localStorage.getItem('code-review-diffstate');
- if (diffstate != 'sidebyside' && diffstate != 'unified')
- return;
- convertAllFileDiffs(diffstate, $('.FileDiff'));
- }
- function isDiffSideBySide(file_diff) {
- return diffState(file_diff) == 'sidebyside';
- }
- function diffState(file_diff) {
- var diff_state = $(file_diff).attr('data-diffstate');
- return diff_state || 'unified';
- }
- function unifyLine(line, from, to, contents, classNames, attributes, id) {
- var new_line = unifiedLine(from, to, contents, false, classNames, attributes);
- var old_line = $(line);
- if (!old_line.hasClass('LineContainer'))
- old_line = old_line.parents('.LineContainer');
- var comments = commentsToTransferFor($(document.getElementById(id)));
- old_line.after(comments);
- old_line.replaceWith(new_line);
- }
- function updateDiffLinkVisibility(file_diff) {
- if (diffState(file_diff) == 'unified') {
- $('.side-by-side-link', file_diff).show();
- $('.unify-link', file_diff).hide();
- } else {
- $('.side-by-side-link', file_diff).hide();
- $('.unify-link', file_diff).show();
- }
- }
- function convertAllFileDiffs(diff_type, file_diffs) {
- file_diffs.each(function() {
- convertFileDiff(diff_type, this);
- });
- }
- function convertFileDiff(diff_type, file_diff) {
- if (diffState(file_diff) == diff_type)
- return;
- if (!$('.resizeHandle', file_diff).length)
- $(file_diff).append('<div class="resizeHandle"></div>');
- $(file_diff).removeClass('sidebyside unified');
- $(file_diff).addClass(diff_type);
- $(file_diff).attr('data-diffstate', diff_type);
- updateDiffLinkVisibility(file_diff);
- $('.context', file_diff).each(function() {
- convertLine(diff_type, this);
- });
- $('.shared .Line', file_diff).each(function() {
- convertLine(diff_type, this);
- });
- $('.ExpansionLine', file_diff).each(function() {
- convertExpansionLine(diff_type, this);
- });
- }
- function convertLine(diff_type, line) {
- var convert_function = diff_type == 'sidebyside' ? sideBySideifyLine : unifyLine;
- var from = fromLineNumber(line);
- var to = toLineNumber(line);
- var contents = $('.text', line).first();
- var classNames = classNamesForMovingLine(line);
- var attributes = attributesForMovingLine(line);
- var id = line.id;
- convert_function(line, from, to, contents, classNames, attributes, id)
- }
- function classNamesForMovingLine(line) {
- var classParts = line.className.split(' ');
- var classBuffer = [];
- for (var i = 0; i < classParts.length; i++) {
- var part = classParts[i];
- if (part != 'LineContainer' && part != 'Line')
- classBuffer.push(part);
- }
- return classBuffer.join(' ');
- }
- function attributesForMovingLine(line) {
- var attributesBuffer = ['id=' + line.id];
- // Make sure to keep all data- attributes.
- $(line.attributes).each(function() {
- if (this.name.indexOf('data-') == 0)
- attributesBuffer.push(this.name + '=' + this.value);
- });
- return attributesBuffer.join(' ');
- }
- function sideBySideifyLine(line, from, to, contents, classNames, attributes, id) {
- var from_class = '';
- var to_class = '';
- var from_attributes = '';
- var to_attributes = '';
- // Clone the contents so we have two copies we can put back in the DOM.
- var from_contents = contents.clone(true);
- var to_contents = contents;
- var container_class = 'LineContainer';
- var container_attributes = '';
- if (from && !to) { // This is a remove line.
- from_class = classNames;
- from_attributes = attributes;
- to_contents = '';
- } else if (to && !from) { // This is an add line.
- to_class = classNames;
- to_attributes = attributes;
- from_contents = '';
- } else {
- container_attributes = attributes;
- container_class += ' Line ' + classNames;
- }
- var new_line = $('<div ' + container_attributes + ' class="' + container_class + '"></div>');
- new_line.append(lineSide('from', from_contents, false, from, from_attributes, from_class));
- new_line.append(lineSide('to', to_contents, false, to, to_attributes, to_class));
- $(line).replaceWith(new_line);
- if (!line.classList.contains('context')) {
- var line = $(document.getElementById(id));
- line.after(commentsToTransferFor(line));
- }
- }
- function convertExpansionLine(diff_type, line) {
- var convert_function = diff_type == 'sidebyside' ? sideBySideExpansionLine : unifiedExpansionLine;
- var contents = $('.text', line).first();
- var from = fromLineNumber(line);
- var to = toLineNumber(line);
- var new_line = convert_function(from, to, contents);
- $(line).replaceWith(new_line);
- }
- function commentsToTransferFor(line) {
- var fragment = document.createDocumentFragment();
- previousCommentsFor(line).each(function() {
- fragment.appendChild(this);
- });
- var active_comments = activeCommentFor(line);
- var num_active_comments = active_comments.size();
- if (num_active_comments > 0) {
- if (num_active_comments > 1)
- console.log('ERROR: There is more than one active comment for ' + line.attr('id') + '.');
- var parent = active_comments[0].parentNode;
- var frozenComment = parent.nextSibling;
- fragment.appendChild(parent);
- fragment.appendChild(frozenComment);
- }
- return fragment;
- }
- function discardComment(comment_block) {
- var line_id = $(comment_block).find('textarea').attr('data-comment-for');
- var line = $('#' + line_id)
- $(comment_block).slideUp('fast', function() {
- $(this).remove();
- line.removeAttr('data-has-comment');
- trimCommentContextToBefore(line, line_id);
- saveDraftComments();
- });
- }
- function handleUnfreezeComment() {
- unfreezeComment(this);
- }
- function unfreezeComment(comment) {
- var unfrozen_comment = $(comment).prev();
- unfrozen_comment.show();
- $(comment).remove();
- unfrozen_comment.find('textarea')[0].focus();
- }
- function showFileDiffLinks() {
- $('.LinkContainer', this).each(function() { this.style.opacity = 1; });
- }
- function hideFileDiffLinks() {
- $('.LinkContainer', this).each(function() { this.style.opacity = 0; });
- }
- function handleDiscardComment() {
- discardComment($(this).parents('.comment'));
- }
-
- function handleAcceptComment() {
- acceptComment($(this).parents('.comment'));
- }
-
- function acceptComment(comment) {
- var frozen_comment = freezeComment($(comment));
- focusOn(frozen_comment);
- saveDraftComments();
- return frozen_comment;
- }
- $('.FileDiff').live('mouseenter', showFileDiffLinks);
- $('.FileDiff').live('mouseleave', hideFileDiffLinks);
- $('.side-by-side-link').live('click', handleSideBySideLinkClick);
- $('.unify-link').live('click', handleUnifyLinkClick);
- $('.ExpandLink').live('click', handleExpandLinkClick);
- $('.frozenComment').live('click', handleUnfreezeComment);
- $('.comment .discard').live('click', handleDiscardComment);
- $('.comment .ok').live('click', handleAcceptComment);
- $('.more').live('click', showMoreHelp);
- $('.more-help .winter').live('click', hideMoreHelp);
- function freezeComment(comment_block) {
- var comment_textarea = comment_block.find('textarea');
- if (comment_textarea.val().trim() == '') {
- discardComment(comment_block);
- return;
- }
- var line_id = comment_textarea.attr('data-comment-for');
- var line = $('#' + line_id)
- var frozen_comment = $('<div class="frozenComment"></div>').text(comment_textarea.val());
- findCommentBlockFor(line).hide().after(frozen_comment);
- return frozen_comment;
- }
- function focusOn(node, opt_is_backward) {
- if (node.length == 0)
- return;
- // Give a tabindex so the element can receive actual browser focus.
- // -1 makes the element focusable without actually putting in in the tab order.
- node.attr('tabindex', -1);
- node.focus();
- // Remove the tabindex on blur to avoid having the node be mouse-focusable.
- node.bind('blur', function() { node.removeAttr('tabindex'); });
-
- var node_top = node.offset().top;
- var is_top_offscreen = node_top <= $(document).scrollTop();
-
- var half_way_point = $(document).scrollTop() + window.innerHeight / 2;
- var is_top_past_halfway = opt_is_backward ? node_top < half_way_point : node_top > half_way_point;
- if (is_top_offscreen || is_top_past_halfway)
- $(document).scrollTop(node_top - window.innerHeight / 2);
- }
- function visibleNodeFilterFunction(is_backward) {
- var y = is_backward ? $('#toolbar')[0].offsetTop - 1 : 0;
- var x = window.innerWidth / 2;
- var reference_element = document.elementFromPoint(x, y);
- if (reference_element.nodeName == 'HTML' || reference_element.nodeName == 'BODY') {
- // In case we hit test a margin between file diffs, shift a fudge factor and try again.
- // FIXME: Is there a better way to do this?
- var file_diffs = $('.FileDiff');
- var first_diff = file_diffs.first();
- var second_diff = $(file_diffs[1]);
- var distance_between_file_diffs = second_diff.position().top - first_diff.position().top - first_diff.height();
- if (is_backward)
- y -= distance_between_file_diffs;
- else
- y += distance_between_file_diffs;
- reference_element = document.elementFromPoint(x, y);
- }
- if (reference_element.nodeName == 'HTML' || reference_element.nodeName == 'BODY')
- return null;
-
- return function(node) {
- var compare = reference_element.compareDocumentPosition(node[0]);
- if (is_backward)
- return compare & Node.DOCUMENT_POSITION_PRECEDING;
- return compare & Node.DOCUMENT_POSITION_FOLLOWING;
- }
- }
- function focusNext(filter, direction) {
- var focusable_nodes = $('a,.Line,.frozenComment,.previousComment,.DiffBlock,.overallComments').filter(function() {
- return !$(this).hasClass('DiffBlock') || $('.add,.remove', this).size();
- });
- var is_backward = direction == DIRECTION.BACKWARD;
- var index = focusable_nodes.index($(document.activeElement));
-
- var extra_filter = null;
- if (index == -1) {
- if (is_backward)
- index = focusable_nodes.length;
- extra_filter = visibleNodeFilterFunction(is_backward);
- }
- var offset = is_backward ? -1 : 1;
- var end = is_backward ? -1 : focusable_nodes.size();
- for (var i = index + offset; i != end; i = i + offset) {
- var node = $(focusable_nodes[i]);
- if (filter(node) && (!extra_filter || extra_filter(node))) {
- focusOn(node, is_backward);
- return true;
- }
- }
- return false;
- }
- var DIRECTION = {FORWARD: 1, BACKWARD: 2};
- function isComment(node) {
- return node.hasClass('frozenComment') || node.hasClass('previousComment') || node.hasClass('overallComments');
- }
-
- function isDiffBlock(node) {
- return node.hasClass('DiffBlock');
- }
-
- function isLine(node) {
- return node.hasClass('Line');
- }
- function commentTextareaForKeyTarget(key_target) {
- if (key_target.nodeName == 'TEXTAREA')
- return $(key_target);
- var comment_textarea = $(document.activeElement).prev().find('textarea');
- if (!comment_textarea.size())
- return null;
- return comment_textarea;
- }
- function extendCommentContextUp(key_target) {
- var comment_textarea = commentTextareaForKeyTarget(key_target);
- if (!comment_textarea)
- return;
- var comment_base_line = comment_textarea.attr('data-comment-for');
- var diff_section = diffSectionFor(comment_textarea);
- var lines = $('.Line', diff_section);
- for (var i = 0; i < lines.length - 1; i++) {
- if (hasDataCommentBaseLine(lines[i + 1], comment_base_line)) {
- addDataCommentBaseLine(lines[i], comment_base_line);
- break;
- }
- }
- }
- function shrinkCommentContextDown(key_target) {
- var comment_textarea = commentTextareaForKeyTarget(key_target);
- if (!comment_textarea)
- return;
- var comment_base_line = comment_textarea.attr('data-comment-for');
- var diff_section = diffSectionFor(comment_textarea);
- var lines = contextLinesFor(comment_base_line, diff_section);
- if (lines.size() > 1)
- removeDataCommentBaseLine(lines[0], comment_base_line);
- }
- function handleModifyContextKey(e) {
- var handled = false;
- if (e.shiftKey && e.ctrlKey) {
- switch (e.keyCode) {
- case KEY_CODE.up:
- extendCommentContextUp(e.target);
- handled = true;
- break;
- case KEY_CODE.down:
- shrinkCommentContextDown(e.target);
- handled = true;
- break;
- }
- }
- if (handled)
- e.preventDefault();
- return handled;
- }
- $('textarea').live('keydown', function(e) {
- if (handleModifyContextKey(e))
- return;
- if (e.keyCode == KEY_CODE.escape)
- handleEscapeKeyInTextarea(this);
- });
- $('body').live('keydown', function(e) {
- // FIXME: There's got to be a better way to avoid seeing these keypress
- // events.
- if (e.target.nodeName == 'TEXTAREA')
- return;
- // Don't want to override browser shortcuts like ctrl+r.
- if (e.metaKey || e.ctrlKey)
- return;
- if (handleModifyContextKey(e))
- return;
- var handled = false;
- switch (e.keyCode) {
- case KEY_CODE.r:
- $('.review select').focus();
- handled = true;
- break;
- case KEY_CODE.n:
- handled = focusNext(isComment, DIRECTION.FORWARD);
- break;
- case KEY_CODE.p:
- handled = focusNext(isComment, DIRECTION.BACKWARD);
- break;
- case KEY_CODE.j:
- if (e.shiftKey)
- handled = focusNext(isLine, DIRECTION.FORWARD);
- else
- handled = focusNext(isDiffBlock, DIRECTION.FORWARD);
- break;
- case KEY_CODE.k:
- if (e.shiftKey)
- handled = focusNext(isLine, DIRECTION.BACKWARD);
- else
- handled = focusNext(isDiffBlock, DIRECTION.BACKWARD);
- break;
-
- case KEY_CODE.enter:
- handled = handleEnterKey();
- break;
-
- case KEY_CODE.escape:
- hideMoreHelp();
- handled = true;
- break;
- }
-
- if (handled)
- e.preventDefault();
- });
-
- function handleEscapeKeyInTextarea(textarea) {
- var comment = $(textarea).parents('.comment');
- if (comment.size())
- acceptComment(comment);
- textarea.blur();
- document.body.focus();
- }
-
- function handleEnterKey() {
- if (document.activeElement.nodeName == 'BODY')
- return;
- var focused = $(document.activeElement);
- if (focused.hasClass('frozenComment')) {
- unfreezeComment(focused);
- return true;
- }
-
- if (focused.hasClass('overallComments')) {
- openOverallComments();
- focused.find('textarea')[0].focus();
- return true;
- }
-
- if (focused.hasClass('previousComment')) {
- addCommentField(focused);
- return true;
- }
- var lines = focused.hasClass('Line') ? focused : $('.Line', focused);
- var last = lines.last();
- if (last.attr('data-has-comment')) {
- unfreezeCommentFor(last);
- return true;
- }
- addCommentForLines(lines);
- return true;
- }
- function contextLinesFor(comment_base_lines, file_diff) {
- var base_lines = comment_base_lines.split(' ');
- return $('div[data-comment-base-line]', file_diff).filter(function() {
- return $(this).attr('data-comment-base-line').split(' ').some(function(item) {
- return base_lines.indexOf(item) != -1;
- });
- });
- }
- function numberFrom(line_id) {
- return Number(line_id.replace('line', ''));
- }
- function trimCommentContextToBefore(line, comment_base_line) {
- var line_to_trim_to = numberFrom(line.attr('id'));
- contextLinesFor(comment_base_line, fileDiffFor(line)).each(function() {
- var id = $(this).attr('id');
- if (numberFrom(id) > line_to_trim_to)
- return;
- if (!$('[data-comment-for=' + comment_base_line + ']').length)
- removeDataCommentBaseLine(this, comment_base_line);
- });
- }
- var drag_select_start_index = -1;
- function lineOffsetFrom(line, offset) {
- var file_diff = line.parents('.FileDiff');
- var all_lines = $('.Line', file_diff);
- var index = all_lines.index(line);
- return $(all_lines[index + offset]);
- }
- function previousLineFor(line) {
- return lineOffsetFrom(line, -1);
- }
- function nextLineFor(line) {
- return lineOffsetFrom(line, 1);
- }
- $('.resizeHandle').live('mousedown', function(event) {
- file_diff_being_resized = $(this).parent('.FileDiff');
- });
- function generateFileDiffResizeStyleElement() {
- // FIXME: Once we support calc, we can replace this with something that uses the attribute value.
- var styleText = '';
- for (var i = minLeftSideRatio; i <= maxLeftSideRatio; i++) {
- // FIXME: Once we support calc, put the resize handle at calc(i% - 5) so it doesn't cover up
- // the right-side line numbers.
- styleText += '.FileDiff[leftsidewidth="' + i + '"] .resizeHandle {' +
- 'left: ' + i + '%' +
- '}' +
- '.FileDiff[leftsidewidth="' + i + '"] .LineSide:first-child,' +
- '.FileDiff[leftsidewidth="' + i + '"].sidebyside .DiffBlockPart.remove {' +
- 'width:' + i + '%;' +
- '}' +
- '.FileDiff[leftsidewidth="' + i + '"] .LineSide:last-child,' +
- '.FileDiff[leftsidewidth="' + i + '"].sidebyside .DiffBlockPart.add {' +
- 'width:' + (100 - i) + '%;' +
- '}';
- }
- var styleElement = document.createElement('style');
- styleElement.innerText = styleText;
- document.head.appendChild(styleElement);
- }
- $(document).bind('mousemove', function(event) {
- if (!file_diff_being_resized)
- return;
- var ratio = event.pageX / window.innerWidth;
- var percentage = Math.floor(ratio * 100);
- if (percentage < minLeftSideRatio)
- percentage = minLeftSideRatio;
- if (percentage > maxLeftSideRatio)
- percentage = maxLeftSideRatio;
- file_diff_being_resized.attr('leftsidewidth', percentage);
- event.preventDefault();
- });
- $(document).bind('mouseup', function(event) {
- file_diff_being_resized = null;
- processSelectedLines();
- });
- $('.lineNumber').live('click', function(e) {
- var line = lineFromLineDescendant($(this));
- if (line.hasClass('commentContext')) {
- var previous_line = previousLineFor(line);
- if (previous_line[0])
- trimCommentContextToBefore(previous_line, line.attr('data-comment-base-line'));
- } else if (e.shiftKey)
- extendCommentContextTo(line);
- }).live('mousedown', function(e) {
- // preventDefault to avoid selecting text when dragging to select comment context lines.
- // FIXME: should we use user-modify CSS instead?
- e.preventDefault();
- if (e.shiftKey)
- return;
- var line = lineFromLineDescendant($(this));
- if (line.hasClass('context'))
- return;
- drag_select_start_index = numberFrom(line.attr('id'));
- line.addClass('selected');
- });
- $('.LineContainer:not(.context)').live('mouseenter', function(e) {
- if (drag_select_start_index == -1 || e.shiftKey)
- return;
- selectToLineContainer(this);
- }).live('mouseup', function(e) {
- if (drag_select_start_index == -1 || e.shiftKey)
- return;
- selectToLineContainer(this);
- processSelectedLines();
- });
- function extendCommentContextTo(line) {
- var diff_section = diffSectionFor(line);
- var lines = $('.Line', diff_section);
- var lines_to_modify = [];
- var have_seen_start_line = false;
- var data_comment_base_line = null;
- lines.each(function() {
- if (data_comment_base_line)
- return;
- have_seen_start_line = have_seen_start_line || this == line[0];
-
- if (have_seen_start_line) {
- if ($(this).hasClass('commentContext'))
- data_comment_base_line = $(this).attr('data-comment-base-line');
- else
- lines_to_modify.push(this);
- }
- });
-
- // There is no comment context to extend.
- if (!data_comment_base_line)
- return;
-
- $(lines_to_modify).each(function() {
- $(this).addClass('commentContext');
- $(this).attr('data-comment-base-line', data_comment_base_line);
- });
- }
- function selectTo(focus_index) {
- var selected = $('.selected').removeClass('selected');
- var is_backward = drag_select_start_index > focus_index;
- var current_index = is_backward ? focus_index : drag_select_start_index;
- var last_index = is_backward ? drag_select_start_index : focus_index;
- while (current_index <= last_index) {
- $('#line' + current_index).addClass('selected')
- current_index++;
- }
- }
- function selectToLineContainer(line_container) {
- var line = lineFromLineContainer(line_container);
- // Ensure that the selected lines are all contained in the same DiffSection.
- var selected_lines = $('.selected');
- var selected_diff_section = diffSectionFor(selected_lines.first());
- var new_diff_section = diffSectionFor(line);
- if (new_diff_section[0] != selected_diff_section[0]) {
- var lines = $('.Line', selected_diff_section);
- if (numberFrom(selected_lines.first().attr('id')) == drag_select_start_index)
- line = lines.last();
- else
- line = lines.first();
- }
-
- selectTo(numberFrom(line.attr('id')));
- }
- function processSelectedLines() {
- drag_select_start_index = -1;
- addCommentForLines($('.selected'));
- }
-
- function addCommentForLines(lines) {
- if (!lines.size())
- return;
- var already_has_comment = lines.last().hasClass('commentContext');
- var comment_base_line;
- if (already_has_comment)
- comment_base_line = lines.last().attr('data-comment-base-line');
- else {
- var last = lineFromLineDescendant(lines.last());
- addCommentFor($(last));
- comment_base_line = last.attr('id');
- }
- lines.each(function() {
- addDataCommentBaseLine(this, comment_base_line);
- $(this).removeClass('selected');
- });
- saveDraftComments();
- }
- function hasDataCommentBaseLine(line, id) {
- var val = $(line).attr('data-comment-base-line');
- if (!val)
- return false;
- var parts = val.split(' ');
- for (var i = 0; i < parts.length; i++) {
- if (parts[i] == id)
- return true;
- }
- return false;
- }
- function addDataCommentBaseLine(line, id) {
- $(line).addClass('commentContext');
- if (hasDataCommentBaseLine(line, id))
- return;
- var val = $(line).attr('data-comment-base-line');
- var parts = val ? val.split(' ') : [];
- parts.push(id);
- $(line).attr('data-comment-base-line', parts.join(' '));
- }
- function removeDataCommentBaseLine(line, comment_base_lines) {
- var val = $(line).attr('data-comment-base-line');
- if (!val)
- return;
- var base_lines = comment_base_lines.split(' ');
- var parts = val.split(' ');
- var new_parts = [];
- for (var i = 0; i < parts.length; i++) {
- if (base_lines.indexOf(parts[i]) == -1)
- new_parts.push(parts[i]);
- }
- var new_comment_base_line = new_parts.join(' ');
- if (new_comment_base_line)
- $(line).attr('data-comment-base-line', new_comment_base_line);
- else {
- $(line).removeAttr('data-comment-base-line');
- $(line).removeClass('commentContext');
- }
- }
- function lineFromLineDescendant(descendant) {
- return descendant.hasClass('Line') ? descendant : descendant.parents('.Line');
- }
- function lineContainerFromDescendant(descendant) {
- return descendant.hasClass('LineContainer') ? descendant : descendant.parents('.LineContainer');
- }
- function lineFromLineContainer(lineContainer) {
- var line = $(lineContainer);
- if (!line.hasClass('Line'))
- line = $('.Line', line);
- return line;
- }
- function contextSnippetFor(line, indent) {
- var snippets = []
- contextLinesFor(line.attr('id'), fileDiffFor(line)).each(function() {
- var action = ' ';
- if ($(this).hasClass('add'))
- action = '+';
- else if ($(this).hasClass('remove'))
- action = '-';
- snippets.push(indent + action + textContentsFor(this));
- });
- return snippets.join('\n');
- }
- function fileNameFor(line) {
- return fileDiffFor(line).find('h1').text();
- }
- function indentFor(depth) {
- return (new Array(depth + 1)).join('>') + ' ';
- }
- function snippetFor(line, indent) {
- var file_name = fileNameFor(line);
- var line_number = line.hasClass('remove') ? '-' + fromLineNumber(line[0]) : toLineNumber(line[0]);
- return indent + file_name + ':' + line_number + '\n' + contextSnippetFor(line, indent);
- }
- function quotePreviousComments(comments) {
- var quoted_comments = [];
- var depth = comments.size();
- comments.each(function() {
- var indent = indentFor(depth--);
- var text = $(this).children('.content').text();
- quoted_comments.push(indent + '\n' + indent + text.split('\n').join('\n' + indent));
- });
- return quoted_comments.join('\n');
- }
- $('#comment_form .winter').live('click', hideCommentForm);
- function serializedComments() {
- var comments_in_context = []
- forEachLine(function(line) {
- if (line.attr('data-has-comment') != 'true')
- return;
- var comment = findCommentBlockFor(line).children('textarea').val().trim();
- if (comment == '')
- return;
- var previous_comments = previousCommentsFor(line);
- var snippet = snippetFor(line, indentFor(previous_comments.size() + 1));
- var quoted_comments = quotePreviousComments(previous_comments);
- var comment_with_context = [];
- comment_with_context.push(snippet);
- if (quoted_comments != '')
- comment_with_context.push(quoted_comments);
- comment_with_context.push('\n' + comment);
- comments_in_context.push(comment_with_context.join('\n'));
- });
- var comment = $('.overallComments textarea').val().trim();
- if (comment != '')
- comment += '\n\n';
- comment += comments_in_context.join('\n\n');
- if (comments_in_context.length > 0)
- comment = 'View in context: ' + window.location + '\n\n' + comment;
- return comment;
- }
- function fillInReviewForm() {
- var review_form = $('#reviewform').contents();
- review_form.find('#comment').val(serializedComments());
- review_form.find('#flags select').each(function() {
- var control = findControlForFlag(this);
- if (!control.size())
- return;
- $(this).attr('selectedIndex', control.attr('selectedIndex'));
- });
- }
- function showCommentForm() {
- $('#comment_form').removeClass('inactive');
- $('#reviewform').contents().find('#submitBtn').focus();
- }
-
- function hideCommentForm() {
- $('#comment_form').addClass('inactive');
-
- // Make sure the top document has focus so key events don't keep going to the review form.
- document.body.tabIndex = -1;
- document.body.focus();
- }
- $('#preview_comments').live('click', function() {
- fillInReviewForm();
- showCommentForm();
- });
- $('#post_comments').live('click', function() {
- fillInReviewForm();
- $('#reviewform').contents().find('form').submit();
- });
-
- if (CODE_REVIEW_UNITTEST) {
- window.DraftCommentSaver = DraftCommentSaver;
- window.addCommentFor = addCommentFor;
- window.addPreviousComment = addPreviousComment;
- window.tracLinks = tracLinks;
- window.crawlDiff = crawlDiff;
- window.convertAllFileDiffs = convertAllFileDiffs;
- window.sanitizeFragmentForCopy = sanitizeFragmentForCopy;
- window.displayPreviousComments = displayPreviousComments;
- window.discardComment = discardComment;
- window.addCommentField = addCommentField;
- window.acceptComment = acceptComment;
- window.appendToolbar = appendToolbar;
- window.eraseDraftComments = eraseDraftComments;
- window.serializedComments = serializedComments;
- window.setFileContents = setFileContents;
- window.unfreezeComment = unfreezeComment;
- window.g_draftCommentSaver = g_draftCommentSaver;
- window.isChangeLog = isChangeLog;
- } else {
- $(document).ready(handleDocumentReady)
- }
- })();
|