|
// Display Jira history changes as colored diffs. |
|
// Paste into the dev console or turn into a bookmarklet. |
|
|
|
|
|
//From https://johnresig.com/files/jsdiff.js |
|
/* |
|
* Javascript Diff Algorithm |
|
* By John Resig (http://ejohn.org/) |
|
* Modified by Chu Alan "sprite" |
|
* |
|
* Released under the MIT license. |
|
* |
|
* More Info: |
|
* http://ejohn.org/projects/javascript-diff-algorithm/ |
|
*/ |
|
|
|
function escape(s) { |
|
var n = s; |
|
n = n.replace(/&/g, "&"); |
|
n = n.replace(/</g, "<"); |
|
n = n.replace(/>/g, ">"); |
|
n = n.replace(/"/g, """); |
|
|
|
return n; |
|
} |
|
|
|
function diffString( o, n ) { |
|
o = o.replace(/\s+$/, ''); |
|
n = n.replace(/\s+$/, ''); |
|
|
|
var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/) ); |
|
var str = ""; |
|
|
|
var oSpace = o.match(/\s+/g); |
|
if (oSpace == null) { |
|
oSpace = ["\n"]; |
|
} else { |
|
oSpace.push("\n"); |
|
} |
|
var nSpace = n.match(/\s+/g); |
|
if (nSpace == null) { |
|
nSpace = ["\n"]; |
|
} else { |
|
nSpace.push("\n"); |
|
} |
|
|
|
if (out.n.length == 0) { |
|
for (var i = 0; i < out.o.length; i++) { |
|
str += '<del>' + escape(out.o[i]) + oSpace[i] + "</del>"; |
|
} |
|
} else { |
|
if (out.n[0].text == null) { |
|
for (n = 0; n < out.o.length && out.o[n].text == null; n++) { |
|
str += '<del>' + escape(out.o[n]) + oSpace[n] + "</del>"; |
|
} |
|
} |
|
|
|
for ( var i = 0; i < out.n.length; i++ ) { |
|
if (out.n[i].text == null) { |
|
str += '<ins>' + escape(out.n[i]) + nSpace[i] + "</ins>"; |
|
} else { |
|
var pre = ""; |
|
|
|
for (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++ ) { |
|
pre += '<del>' + escape(out.o[n]) + oSpace[n] + "</del>"; |
|
} |
|
str += " " + out.n[i].text + nSpace[i] + pre; |
|
} |
|
} |
|
} |
|
|
|
return str; |
|
} |
|
|
|
function randomColor() { |
|
return "rgb(" + (Math.random() * 100) + "%, " + |
|
(Math.random() * 100) + "%, " + |
|
(Math.random() * 100) + "%)"; |
|
} |
|
function diffString2( o, n ) { |
|
o = o.replace(/\s+$/, ''); |
|
n = n.replace(/\s+$/, ''); |
|
|
|
var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/) ); |
|
|
|
var oSpace = o.match(/\s+/g); |
|
if (oSpace == null) { |
|
oSpace = ["\n"]; |
|
} else { |
|
oSpace.push("\n"); |
|
} |
|
var nSpace = n.match(/\s+/g); |
|
if (nSpace == null) { |
|
nSpace = ["\n"]; |
|
} else { |
|
nSpace.push("\n"); |
|
} |
|
|
|
var os = ""; |
|
var colors = new Array(); |
|
for (var i = 0; i < out.o.length; i++) { |
|
colors[i] = randomColor(); |
|
|
|
if (out.o[i].text != null) { |
|
os += '<span style="background-color: ' +colors[i]+ '">' + |
|
escape(out.o[i].text) + oSpace[i] + "</span>"; |
|
} else { |
|
os += "<del>" + escape(out.o[i]) + oSpace[i] + "</del>"; |
|
} |
|
} |
|
|
|
var ns = ""; |
|
for (var i = 0; i < out.n.length; i++) { |
|
if (out.n[i].text != null) { |
|
ns += '<span style="background-color: ' +colors[out.n[i].row]+ '">' + |
|
escape(out.n[i].text) + nSpace[i] + "</span>"; |
|
} else { |
|
ns += "<ins>" + escape(out.n[i]) + nSpace[i] + "</ins>"; |
|
} |
|
} |
|
|
|
return { o : os , n : ns }; |
|
} |
|
|
|
function diff( o, n ) { |
|
var ns = new Object(); |
|
var os = new Object(); |
|
|
|
for ( var i = 0; i < n.length; i++ ) { |
|
if ( ns[ n[i] ] == null ) |
|
ns[ n[i] ] = { rows: new Array(), o: null }; |
|
ns[ n[i] ].rows.push( i ); |
|
} |
|
|
|
for ( var i = 0; i < o.length; i++ ) { |
|
if ( os[ o[i] ] == null ) |
|
os[ o[i] ] = { rows: new Array(), n: null }; |
|
os[ o[i] ].rows.push( i ); |
|
} |
|
|
|
for ( var i in ns ) { |
|
if ( ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1 ) { |
|
n[ ns[i].rows[0] ] = { text: n[ ns[i].rows[0] ], row: os[i].rows[0] }; |
|
o[ os[i].rows[0] ] = { text: o[ os[i].rows[0] ], row: ns[i].rows[0] }; |
|
} |
|
} |
|
|
|
for ( var i = 0; i < n.length - 1; i++ ) { |
|
if ( n[i].text != null && n[i+1].text == null && n[i].row + 1 < o.length && o[ n[i].row + 1 ].text == null && |
|
n[i+1] == o[ n[i].row + 1 ] ) { |
|
n[i+1] = { text: n[i+1], row: n[i].row + 1 }; |
|
o[n[i].row+1] = { text: o[n[i].row+1], row: i + 1 }; |
|
} |
|
} |
|
|
|
for ( var i = n.length - 1; i > 0; i-- ) { |
|
if ( n[i].text != null && n[i-1].text == null && n[i].row > 0 && o[ n[i].row - 1 ].text == null && |
|
n[i-1] == o[ n[i].row - 1 ] ) { |
|
n[i-1] = { text: n[i-1], row: n[i].row - 1 }; |
|
o[n[i].row-1] = { text: o[n[i].row-1], row: i - 1 }; |
|
} |
|
} |
|
|
|
return { o: o, n: n }; |
|
} |
|
|
|
/* ------------------------------- |
|
end https://johnresig.com/files/jsdiff.js |
|
------------------------------- */ |
|
|
|
|
|
|
|
|
|
/* styleText like '.cssClass { color: #F00; }' |
|
from https://stackoverflow.com/a/1720483/4176104 */ |
|
function createCssStyle(styleText){ |
|
var style = document.createElement('style'); |
|
style.type = 'text/css'; |
|
style.innerHTML = styleText; |
|
document.getElementsByTagName('head')[0].appendChild(style); |
|
} |
|
|
|
var parentDivs = document.querySelectorAll('div[id^="changehistory"].issue-data-block'); |
|
|
|
for (var i = 0; i < parentDivs.length; i++) { |
|
var parentDiv = parentDivs[i]; |
|
|
|
var changeHistory = parentDiv.querySelector('.changehistory'); |
|
|
|
if (changeHistory.querySelector('.activity-old-val') == null) { |
|
continue; |
|
} |
|
|
|
var fieldName = changeHistory.querySelector('.activity-name').textContent; |
|
|
|
var oldVal = changeHistory.querySelector('.activity-old-val').textContent |
|
var newVal = changeHistory.querySelector('.activity-new-val').textContent |
|
|
|
var diffDiv = parentDiv.querySelector('.changehistory.action-body'); |
|
|
|
diffDiv.innerHTML = '<strong>' + fieldName + '</strong><br /><br />' + |
|
diffString(oldVal, newVal).replace(/\n\n/g, "<br />"); |
|
} |
|
|
|
createCssStyle('ins { background-color: #BFB; }'); |
|
createCssStyle('del { background-color: #FAA; }'); |
|
|
|
// if you use a dark theme |
|
// createCssStyle('ins { background-color: #040; }'); |
|
// createCssStyle('del { background-color: #400; }'); |
comment for dumping screenshots: