blame_incremental.js [plain text]
var projectUrl;
var commits = {};
function Commit(sha1) {
if (this instanceof Commit) {
this.sha1 = sha1;
this.nprevious = 0;
} else {
return new Commit(sha1);
}
}
var blamedLines = 0;
var totalLines = '???';
var div_progress_bar;
var div_progress_info;
function countLines() {
var table =
document.getElementById('blame_table') ||
document.getElementsByTagName('table')[0];
if (table) {
return table.getElementsByTagName('tr').length - 1; } else {
return '...';
}
}
function updateProgressInfo() {
if (!div_progress_info) {
div_progress_info = document.getElementById('progress_info');
}
if (!div_progress_bar) {
div_progress_bar = document.getElementById('progress_bar');
}
if (!div_progress_info && !div_progress_bar) {
return;
}
var percentage = Math.floor(100.0*blamedLines/totalLines);
if (div_progress_info) {
div_progress_info.firstChild.data = blamedLines + ' / ' + totalLines +
' (' + padLeftStr(percentage, 3, '\u00A0') + '%)';
}
if (div_progress_bar) {
//div_progress_bar.setAttribute('style', 'width: '+percentage+'%;');
div_progress_bar.style.width = percentage + '%';
}
}
var t_interval_server = '';
var cmds_server = '';
var t0 = new Date();
/**
* write how much it took to generate data, and to run script
*
* @globals t0, t_interval_server, cmds_server
*/
function writeTimeInterval() {
var info_time = document.getElementById('generating_time');
if (!info_time || !t_interval_server) {
return;
}
var t1 = new Date();
info_time.firstChild.data += ' + (' +
t_interval_server + ' sec server blame_data / ' +
(t1.getTime() - t0.getTime())/1000 + ' sec client JavaScript)';
var info_cmds = document.getElementById('generating_cmd');
if (!info_time || !cmds_server) {
return;
}
info_cmds.firstChild.data += ' + ' + cmds_server;
}
function errorInfo(str) {
if (!div_progress_info) {
div_progress_info = document.getElementById('progress_info');
}
if (div_progress_info) {
div_progress_info.className = 'error';
div_progress_info.firstChild.data = str;
}
}
var colorRe = /\bcolor([0-9]*)\b/;
function getColorNo(tr) {
if (!tr) {
return null;
}
var className = tr.className;
if (className) {
var match = colorRe.exec(className);
if (match) {
return parseInt(match[1], 10);
}
}
return null;
}
var colorsFreq = [0, 0, 0];
function chooseColorNoFrom() {
var colorNo = arguments[0];
for (var i = 1; i < arguments.length; i++) {
if (colorsFreq[arguments[i]-1] < colorsFreq[colorNo-1]) {
colorNo = arguments[i];
}
}
colorsFreq[colorNo-1]++;
return colorNo;
}
function findColorNo(tr_prev, tr_next) {
var color_prev = getColorNo(tr_prev);
var color_next = getColorNo(tr_next);
if (!color_prev && !color_next) {
return chooseColorNoFrom(1,2,3);
}
var color;
if (color_prev === color_next) {
color = color_prev; } else if (!color_prev) {
color = color_next;
} else if (!color_next) {
color = color_prev;
}
if (color) {
return chooseColorNoFrom((color % 3) + 1, ((color+1) % 3) + 1);
}
return (3 - ((color_prev + color_next) % 3));
}
function isStartOfGroup(tr) {
return tr.firstChild.className === 'sha1';
}
function fixColorsAndGroups() {
var colorClasses = ['light', 'dark'];
var linenum = 1;
var tr, prev_group;
var colorClass = 0;
var table =
document.getElementById('blame_table') ||
document.getElementsByTagName('table')[0];
while ((tr = document.getElementById('l'+linenum))) {
if (isStartOfGroup(tr, linenum, document)) {
if (prev_group &&
prev_group.firstChild.firstChild.href ===
tr.firstChild.firstChild.href) {
var prev_rows = prev_group.firstChild.rowSpan || 1;
var curr_rows = tr.firstChild.rowSpan || 1;
prev_group.firstChild.rowSpan = prev_rows + curr_rows;
tr.deleteCell(0); } else {
colorClass = (colorClass + 1) % 2;
prev_group = tr;
}
}
var tr_class = tr.className;
tr.className = tr_class.replace(colorRe, colorClasses[colorClass]);
linenum++;
}
}
function handleLine(commit, group) {
var resline = group.resline;
if (!commit.info) {
commit.info = commit.author + ', ' +
formatDateISOLocal(commit.authorTime, commit.authorTimezone);
}
var colorNo = findColorNo(
document.getElementById('l'+(resline-1)),
document.getElementById('l'+(resline+group.numlines))
);
for (var i = 0; i < group.numlines; i++, resline++) {
var tr = document.getElementById('l'+resline);
if (!tr) {
break;
}
var td_sha1 = tr.firstChild;
var a_sha1 = td_sha1.firstChild;
var a_linenr = td_sha1.nextSibling.firstChild;
var tr_class = '';
if (colorNo !== null) {
tr_class = 'color'+colorNo;
}
if (commit.boundary) {
tr_class += ' boundary';
}
if (commit.nprevious === 0) {
tr_class += ' no-previous';
} else if (commit.nprevious > 1) {
tr_class += ' multiple-previous';
}
tr.className = tr_class;
if (i === 0) {
td_sha1.title = commit.info;
td_sha1.rowSpan = group.numlines;
a_sha1.href = projectUrl + 'a=commit;h=' + commit.sha1;
if (a_sha1.firstChild) {
a_sha1.firstChild.data = commit.sha1.substr(0, 8);
} else {
a_sha1.appendChild(
document.createTextNode(commit.sha1.substr(0, 8)));
}
if (group.numlines >= 2) {
var fragment = document.createDocumentFragment();
var br = document.createElement("br");
var match = commit.author.match(/\b([A-Z])\B/g);
if (match) {
var text = document.createTextNode(
match.join(''));
}
if (br && text) {
var elem = fragment || td_sha1;
elem.appendChild(br);
elem.appendChild(text);
if (fragment) {
td_sha1.appendChild(fragment);
}
}
}
} else {
tr.deleteCell(0); }
var linenr_commit =
('previous' in commit ? commit.previous : commit.sha1);
var linenr_filename =
('file_parent' in commit ? commit.file_parent : commit.filename);
a_linenr.href = projectUrl + 'a=blame_incremental' +
';hb=' + linenr_commit +
';f=' + encodeURIComponent(linenr_filename) +
'#l' + (group.srcline + i);
blamedLines++;
}
}
var sha1Re = /^([0-9a-f]{40}) ([0-9]+) ([0-9]+) ([0-9]+)/;
var infoRe = /^([a-z-]+) ?(.*)/;
var endRe = /^END ?([^ ]*) ?(.*)/;
var curCommit = new Commit();
var curGroup = {};
function processBlameLines(lines) {
var match;
for (var i = 0, len = lines.length; i < len; i++) {
if ((match = sha1Re.exec(lines[i]))) {
var sha1 = match[1];
var srcline = parseInt(match[2], 10);
var resline = parseInt(match[3], 10);
var numlines = parseInt(match[4], 10);
var c = commits[sha1];
if (!c) {
c = new Commit(sha1);
commits[sha1] = c;
}
curCommit = c;
curGroup.srcline = srcline;
curGroup.resline = resline;
curGroup.numlines = numlines;
} else if ((match = infoRe.exec(lines[i]))) {
var info = match[1];
var data = match[2];
switch (info) {
case 'filename':
curCommit.filename = unquote(data);
handleLine(curCommit, curGroup);
updateProgressInfo();
break;
case 'author':
curCommit.author = data;
break;
case 'author-time':
curCommit.authorTime = parseInt(data, 10);
break;
case 'author-tz':
curCommit.authorTimezone = data;
break;
case 'previous':
curCommit.nprevious++;
if (!('previous' in curCommit)) {
var parts = data.split(' ', 2);
curCommit.previous = parts[0];
curCommit.file_parent = unquote(parts[1]);
}
break;
case 'boundary':
curCommit.boundary = true;
break;
}
} else if ((match = endRe.exec(lines[i]))) {
t_interval_server = match[1];
cmds_server = match[2];
} else if (lines[i] !== '') {
}
} }
function processData(unprocessed, nextReadPos) {
var lastLineEnd = unprocessed.lastIndexOf('\n');
if (lastLineEnd !== -1) {
var lines = unprocessed.substring(0, lastLineEnd).split('\n');
nextReadPos += lastLineEnd + 1 ;
processBlameLines(lines);
}
return nextReadPos;
}
function handleError(xhr) {
errorInfo('Server error: ' +
xhr.status + ' - ' + (xhr.statusText || 'Error contacting server'));
if (typeof xhr.pollTimer === "number") {
clearTimeout(xhr.pollTimer);
delete xhr.pollTimer;
}
commits = {}; }
function responseLoaded(xhr) {
if (typeof xhr.pollTimer === "number") {
clearTimeout(xhr.pollTimer);
delete xhr.pollTimer;
}
fixColorsAndGroups();
writeTimeInterval();
commits = {}; }
function handleResponse(xhr, fromTimer) {
if (xhr.readyState !== 4 && xhr.readyState !== 3) {
return;
}
try {
if (xhr.readyState === 3 && xhr.status !== 200) {
return;
}
} catch (e) {
return;
}
if (xhr.readyState === 4 && xhr.status !== 200) {
handleError(xhr);
return;
}
if (xhr.responseText === null) {
return;
}
if (xhr.prevDataLength !== xhr.responseText.length) {
xhr.prevDataLength = xhr.responseText.length;
var unprocessed = xhr.responseText.substring(xhr.nextReadPos);
xhr.nextReadPos = processData(unprocessed, xhr.nextReadPos);
}
if (xhr.readyState === 4) {
responseLoaded(xhr);
return;
}
if (fromTimer) {
setTimeout(function () {
handleResponse(xhr, true);
}, 1000);
} else if (typeof xhr.pollTimer === "number") {
clearTimeout(xhr.pollTimer);
delete xhr.pollTimer;
}
}
function startBlame(blamedataUrl, bUrl) {
var xhr = createRequestObject();
if (!xhr) {
errorInfo('ERROR: XMLHttpRequest not supported');
return;
}
t0 = new Date();
projectUrl = bUrl + (bUrl.indexOf('?') === -1 ? '?' : ';');
if ((div_progress_bar = document.getElementById('progress_bar'))) {
div_progress_bar.style.cssText = 'width: 100%;';
}
totalLines = countLines();
updateProgressInfo();
xhr.prevDataLength = -1; xhr.nextReadPos = 0;
xhr.onreadystatechange = function () {
handleResponse(xhr, false);
};
xhr.open('GET', blamedataUrl);
xhr.setRequestHeader('Accept', 'text/plain');
xhr.send(null);
xhr.pollTimer = setTimeout(function () {
handleResponse(xhr, true);
}, 1000);
}