mirror of
https://github.com/gitbucket/gitbucket.git
synced 2025-11-06 21:45:50 +01:00
Implements ignore-whitespace and hilight-syntax on diff.
Improvement experience speed * lazy diff rendering ( it is effective if tha diff has a lot of files ). * some feature implemented by javascript, to implement by css. * some javascript event handlers on each elements move to on parent elements.
This commit is contained in:
@@ -42,15 +42,16 @@
|
|||||||
}
|
}
|
||||||
@diffs.zipWithIndex.map { case (diff, i) =>
|
@diffs.zipWithIndex.map { case (diff, i) =>
|
||||||
<a name="diff-@i"></a>
|
<a name="diff-@i"></a>
|
||||||
<table class="table table-bordered" commitId="@newCommitId" fileName="@diff.newPath">
|
<table class="table table-bordered diff-outside" commitId="@newCommitId" fileName="@diff.newPath" data-diff-id="@i">
|
||||||
<tr>
|
<tr>
|
||||||
<th style="font-weight: normal; line-height: 27px;" class="box-header">
|
<th style="font-weight: normal; line-height: 27px;" class="box-header">
|
||||||
@if(diff.changeType == ChangeType.COPY || diff.changeType == ChangeType.RENAME){
|
@if(diff.changeType == ChangeType.COPY || diff.changeType == ChangeType.RENAME){
|
||||||
<img src="@assets/common/images/diff_move.png"/> @diff.oldPath -> @diff.newPath
|
<img src="@assets/common/images/diff_move.png"/> @diff.oldPath -> @diff.newPath
|
||||||
@if(newCommitId.isDefined){
|
@if(newCommitId.isDefined){
|
||||||
<div class="pull-right align-right">
|
<div class="pull-right align-right">
|
||||||
|
<label class="checkbox" style="display: inline-block;"><input type="checkbox" class="ignore-whitespace" value="1"/>Ignore Space</label>
|
||||||
<label class="checkbox" style="display: inline-block;"><input type="checkbox" class="toggle-notes" checked><span>Show notes</span></label>
|
<label class="checkbox" style="display: inline-block;"><input type="checkbox" class="toggle-notes" checked><span>Show notes</span></label>
|
||||||
<a href="@url(repository)/blob/@newCommitId.get/@diff.newPath" class="btn btn-small">View file @@ @newCommitId.get.substring(0, 10)</a>
|
<a href="@url(repository)/blob/@newCommitId.get/@diff.newPath" class="btn btn-small" title="View the whole file at version @newCommitId.get.substring(0, 10)" data-toggle="tooltip">View</a>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -62,8 +63,9 @@
|
|||||||
} @diff.newPath
|
} @diff.newPath
|
||||||
@if(newCommitId.isDefined){
|
@if(newCommitId.isDefined){
|
||||||
<div class="pull-right align-right">
|
<div class="pull-right align-right">
|
||||||
|
<label class="checkbox" style="display: inline-block;"><input type="checkbox" class="ignore-whitespace" value="1"/>Ignore Space</label>
|
||||||
<label class="checkbox" style="display: inline-block;"><input type="checkbox" class="toggle-notes" checked><span>Show notes</span></label>
|
<label class="checkbox" style="display: inline-block;"><input type="checkbox" class="toggle-notes" checked><span>Show notes</span></label>
|
||||||
<a href="@url(repository)/blob/@newCommitId.get/@diff.newPath" class="btn btn-small">View file @@ @newCommitId.get.substring(0, 10)</a>
|
<a href="@url(repository)/blob/@newCommitId.get/@diff.newPath" class="btn btn-small" title="View the whole file at version @newCommitId.get.substring(0, 10)" data-toggle="tooltip">View</a>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -72,7 +74,7 @@
|
|||||||
@if(oldCommitId.isDefined){
|
@if(oldCommitId.isDefined){
|
||||||
<div class="pull-right align-right">
|
<div class="pull-right align-right">
|
||||||
<label class="checkbox" style="display: inline-block;"><input type="checkbox" class="toggle-notes" checked><span>Show notes</span></label>
|
<label class="checkbox" style="display: inline-block;"><input type="checkbox" class="toggle-notes" checked><span>Show notes</span></label>
|
||||||
<a href="@url(repository)/blob/@oldCommitId.get/@diff.oldPath" class="btn btn-small">View file @@ @oldCommitId.get.substring(0, 10)</a>
|
<a href="@url(repository)/blob/@oldCommitId.get/@diff.oldPath" class="btn btn-small" title="View the whole file at version @oldCommitId.get.substring(0, 10)" data-toggle="tooltip">View</a>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,7 +83,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td style="padding: 0;">
|
<td style="padding: 0;">
|
||||||
@if(diff.newContent != None || diff.oldContent != None){
|
@if(diff.newContent != None || diff.oldContent != None){
|
||||||
<div id="diffText-@i"></div>
|
<div id="diffText-@i" class="diffText"></div>
|
||||||
<textarea id="newText-@i" style="display: none;">@diff.newContent.getOrElse("")</textarea>
|
<textarea id="newText-@i" style="display: none;">@diff.newContent.getOrElse("")</textarea>
|
||||||
<textarea id="oldText-@i" style="display: none;">@diff.oldContent.getOrElse("")</textarea>
|
<textarea id="oldText-@i" style="display: none;">@diff.oldContent.getOrElse("")</textarea>
|
||||||
} else {
|
} else {
|
||||||
@@ -92,7 +94,6 @@
|
|||||||
</table>
|
</table>
|
||||||
}
|
}
|
||||||
<script type="text/javascript" src="@assets/vendors/jsdifflib/difflib.js"></script>
|
<script type="text/javascript" src="@assets/vendors/jsdifflib/difflib.js"></script>
|
||||||
<script type="text/javascript" src="@assets/vendors/jsdifflib/diffview.js"></script>
|
|
||||||
<link href="@assets/vendors/jsdifflib/diffview.css" type="text/css" rel="stylesheet" />
|
<link href="@assets/vendors/jsdifflib/diffview.css" type="text/css" rel="stylesheet" />
|
||||||
<script>
|
<script>
|
||||||
$(function(){
|
$(function(){
|
||||||
@@ -108,16 +109,26 @@ $(function(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Render diffs as unified mode initially
|
// Render diffs as unified mode initially
|
||||||
renderDiffs(1);
|
if(("&"+location.search.substring(1)).indexOf("&w=1")!=-1){
|
||||||
|
$('.ignore-whitespace').prop('checked',true);
|
||||||
|
}
|
||||||
|
window.viewType=1;
|
||||||
|
if(("&"+location.search.substring(1)).indexOf("&diff=split")!=-1){
|
||||||
|
$('.container').removeClass('container').addClass('container-wide');
|
||||||
|
window.viewType=0;
|
||||||
|
}
|
||||||
|
renderDiffs();
|
||||||
|
|
||||||
$('#btn-unified').click(function(){
|
$('#btn-unified').click(function(){
|
||||||
|
window.viewType=1;
|
||||||
$('.container-wide').removeClass('container-wide').addClass('container');
|
$('.container-wide').removeClass('container-wide').addClass('container');
|
||||||
renderDiffs(1);
|
renderDiffs();
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#btn-split').click(function(){
|
$('#btn-split').click(function(){
|
||||||
|
window.viewType=0;
|
||||||
$('.container').removeClass('container').addClass('container-wide');
|
$('.container').removeClass('container').addClass('container-wide');
|
||||||
renderDiffs(0);
|
renderDiffs();
|
||||||
});
|
});
|
||||||
|
|
||||||
$('.toggle-notes').change(function() {
|
$('.toggle-notes').change(function() {
|
||||||
@@ -126,17 +137,10 @@ $(function(){
|
|||||||
}
|
}
|
||||||
$(this).closest('table').find('.not-diff').toggle();
|
$(this).closest('table').find('.not-diff').toggle();
|
||||||
});
|
});
|
||||||
|
$('.ignore-whitespace').change(function() {
|
||||||
|
renderOneDiff($(this).closest("table").find(".diffText"), viewType);
|
||||||
|
});
|
||||||
|
|
||||||
function renderDiffs(viewType){
|
|
||||||
window.viewType = viewType;
|
|
||||||
@diffs.zipWithIndex.map { case (diff, i) =>
|
|
||||||
@if(diff.newContent != None || diff.oldContent != None){
|
|
||||||
if($('#oldText-@i').length > 0){
|
|
||||||
diffUsingJS('oldText-@i', 'newText-@i', 'diffText-@i', viewType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@if(showLineNotes){
|
|
||||||
function getInlineContainer(where) {
|
function getInlineContainer(where) {
|
||||||
if (viewType == 0) {
|
if (viewType == 0) {
|
||||||
if (where === 'new') {
|
if (where === 'new') {
|
||||||
@@ -147,53 +151,10 @@ $(function(){
|
|||||||
}
|
}
|
||||||
return $('<tr class="not-diff"><td colspan="3" class="comment-box-container"></td></tr>');
|
return $('<tr class="not-diff"><td colspan="3" class="comment-box-container"></td></tr>');
|
||||||
}
|
}
|
||||||
$('.inline-comment').each(function(i, v) {
|
|
||||||
var $v = $(v), filename = $v.attr('filename'),
|
|
||||||
oldline = $v.attr('oldline'), newline = $v.attr('newline');
|
|
||||||
if (typeof $('#show-notes')[0] !== 'undefined' && !$('#show-notes')[0].checked) {
|
if (typeof $('#show-notes')[0] !== 'undefined' && !$('#show-notes')[0].checked) {
|
||||||
$(this).hide();
|
$('.inline-comment').hide();
|
||||||
}
|
}
|
||||||
var tmp;
|
$('.diff-outside').on('click','table.diff .add-comment',function() {
|
||||||
var diff;
|
|
||||||
if (typeof oldline !== 'undefined') {
|
|
||||||
if (typeof newline !== 'undefined') {
|
|
||||||
tmp = getInlineContainer();
|
|
||||||
} else {
|
|
||||||
tmp = getInlineContainer('old');
|
|
||||||
}
|
|
||||||
tmp.children('td:first').html($(this).clone().show());
|
|
||||||
diff = $('table[filename="' + filename + '"]');
|
|
||||||
diff.find('table.diff').find('.oldline[line-number=' + oldline + ']')
|
|
||||||
.parent().nextAll(':not(.not-diff):first').before(tmp);
|
|
||||||
} else {
|
|
||||||
tmp = getInlineContainer('new');
|
|
||||||
tmp.children('td:last').html($(this).clone().show());
|
|
||||||
diff = $('table[filename="' + filename + '"]');
|
|
||||||
diff.find('table.diff').find('.newline[line-number=' + newline + ']')
|
|
||||||
.parent().nextAll(':not(.not-diff):first').before(tmp);
|
|
||||||
}
|
|
||||||
if (!diff.find('.toggle-notes').prop('checked')) {
|
|
||||||
tmp.hide();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@if(hasWritePermission) {
|
|
||||||
$('table.diff td').hover(
|
|
||||||
function() {
|
|
||||||
$(this).find('b').css('display', 'inline-block');
|
|
||||||
},
|
|
||||||
function() {
|
|
||||||
$(this).find('b').css('display', 'none');
|
|
||||||
}
|
|
||||||
);
|
|
||||||
$('table.diff th').hover(
|
|
||||||
function() {
|
|
||||||
$(this).nextAll().find('b').first().css('display', 'inline-block');
|
|
||||||
},
|
|
||||||
function() {
|
|
||||||
$(this).nextAll().find('b').first().css('display', 'none');
|
|
||||||
}
|
|
||||||
);
|
|
||||||
$('.add-comment').click(function() {
|
|
||||||
var $this = $(this),
|
var $this = $(this),
|
||||||
$tr = $this.closest('tr'),
|
$tr = $this.closest('tr'),
|
||||||
$check = $this.closest('table:not(.diff)').find('.toggle-notes');
|
$check = $this.closest('table:not(.diff)').find('.toggle-notes');
|
||||||
@@ -240,12 +201,60 @@ $(function(){
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
}).on('click', 'table.diff .btn-default', function() {
|
||||||
$('table.diff').on('click', '.btn-default', function() {
|
|
||||||
$(this).closest('.inline-comment-form').remove();
|
$(this).closest('.inline-comment-form').remove();
|
||||||
});
|
});
|
||||||
|
function renderOneCommitCommentIntoDiff($v, diff){
|
||||||
|
var filename = $v.attr('filename'),
|
||||||
|
oldline = $v.attr('oldline'), newline = $v.attr('newline');
|
||||||
|
var tmp;
|
||||||
|
var diff;
|
||||||
|
if (typeof oldline !== 'undefined') {
|
||||||
|
if (typeof newline !== 'undefined') {
|
||||||
|
tmp = getInlineContainer();
|
||||||
|
} else {
|
||||||
|
tmp = getInlineContainer('old');
|
||||||
|
}
|
||||||
|
tmp.children('td:first').html($v.clone().show());
|
||||||
|
diff.find('table.diff').find('.oldline[line-number=' + oldline + ']')
|
||||||
|
.parent().nextAll(':not(.not-diff):first').before(tmp);
|
||||||
|
} else {
|
||||||
|
tmp = getInlineContainer('new');
|
||||||
|
tmp.children('td:last').html($v.clone().show());
|
||||||
|
diff.find('table.diff').find('.newline[line-number=' + newline + ']')
|
||||||
|
.parent().nextAll(':not(.not-diff):first').before(tmp);
|
||||||
|
}
|
||||||
|
if (!diff.find('.toggle-notes').prop('checked')) {
|
||||||
|
tmp.hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function renderOneDiff(diffText, viewType){
|
||||||
|
var table = diffText.closest("table[data-diff-id]");
|
||||||
|
var i = table.data("diff-id");
|
||||||
|
var ignoreWhiteSpace = table.find('.ignore-whitespace').prop('checked');
|
||||||
|
diffUsingJS('oldText-'+i, 'newText-'+i, diffText.attr('id'), viewType, ignoreWhiteSpace);
|
||||||
|
@if(hasWritePermission) {
|
||||||
|
diffText.find('.body').each(function(){ $('<b class="add-comment">+</b>').prependTo(this); });
|
||||||
|
}
|
||||||
|
@if(showLineNotes){
|
||||||
|
var fileName = table.attr('filename');
|
||||||
|
$('.inline-comment').each(function(i, v) {
|
||||||
|
if($(this).attr('filename')==fileName){
|
||||||
|
renderOneCommitCommentIntoDiff($(this), table);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function renderDiffs(){
|
||||||
|
var i=0, diffs = $('.diffText');
|
||||||
|
function render(){
|
||||||
|
if(diffs[i]){
|
||||||
|
renderOneDiff($(diffs[i]), viewType);
|
||||||
|
i++;
|
||||||
|
setTimeout(render);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
render();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1027,16 +1027,25 @@ td.insert, td.equal, td.delete, td.empty {
|
|||||||
width: 50%;
|
width: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table.diff td.body{
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.diff th.line-num{
|
||||||
|
min-width: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
table.diff .add-comment {
|
table.diff .add-comment {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background: blue;
|
background: #4183c4;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: -7px;
|
left: -7px;
|
||||||
color: white;
|
color: white;
|
||||||
padding: 2px 4px;
|
padding: 2px 4px;
|
||||||
border: solid 1px blue;
|
border: solid 1px #4183c4;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
z-index: 99;
|
z-index: 99;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
table.diff .add-comment:hover {
|
table.diff .add-comment:hover {
|
||||||
@@ -1044,6 +1053,24 @@ table.diff .add-comment:hover {
|
|||||||
top: -1px;
|
top: -1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table.diff tr td.body b.add-comment{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.diff tr:hover td.body b.add-comment{
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container-wide table.diff tr:hover td.body b.add-comment{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container-wide table.diff tr:hover td.body:hover b.add-comment,
|
||||||
|
.container-wide table.diff tr:hover th.line-num:hover + td b.add-comment{
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
table.diff tbody tr.not-diff {
|
table.diff tbody tr.not-diff {
|
||||||
font-family: '"Helvetica Neue", Helvetica, Arial, sans-serif';
|
font-family: '"Helvetica Neue", Helvetica, Arial, sans-serif';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,41 +76,240 @@ function displayErrors(data, elem){
|
|||||||
* @param newTextId {String} element id of new text
|
* @param newTextId {String} element id of new text
|
||||||
* @param outputId {String} element id of output element
|
* @param outputId {String} element id of output element
|
||||||
* @param viewType {Number} 0: split, 1: unified
|
* @param viewType {Number} 0: split, 1: unified
|
||||||
|
* @param ignoreSpace {Number} 0: include, 1: ignore
|
||||||
*/
|
*/
|
||||||
function diffUsingJS(oldTextId, newTextId, outputId, viewType) {
|
function diffUsingJS(oldTextId, newTextId, outputId, viewType, ignoreSpace) {
|
||||||
// get the baseText and newText values from the two textboxes, and split them into lines
|
var render = new JsDiffRender({
|
||||||
var oldText = document.getElementById(oldTextId).value;
|
baseText: document.getElementById(oldTextId).value,
|
||||||
var oldLines = [];
|
newText: document.getElementById(newTextId).value,
|
||||||
if(oldText !== ''){
|
ignoreSpace: ignoreSpace,
|
||||||
oldLines = difflib.stringAsLines(oldText);
|
contextSize: 4
|
||||||
}
|
});
|
||||||
|
var diff = render[viewType==1 ? "unified" : "split"]();
|
||||||
var newText = document.getElementById(newTextId).value;
|
diff.appendTo($('#'+outputId).html(""));
|
||||||
var newLines = [];
|
|
||||||
if(newText !== ''){
|
|
||||||
newLines = difflib.stringAsLines(newText);
|
|
||||||
}
|
|
||||||
|
|
||||||
// create a SequenceMatcher instance that diffs the two sets of lines
|
|
||||||
var sm = new difflib.SequenceMatcher(oldLines, newLines);
|
|
||||||
|
|
||||||
// get the opcodes from the SequenceMatcher instance
|
|
||||||
// opcodes is a list of 3-tuples describing what changes should be made to the base text
|
|
||||||
// in order to yield the new text
|
|
||||||
var opcodes = sm.get_opcodes();
|
|
||||||
var diffoutputdiv = document.getElementById(outputId);
|
|
||||||
while (diffoutputdiv.firstChild) diffoutputdiv.removeChild(diffoutputdiv.firstChild);
|
|
||||||
|
|
||||||
// build the diff view and add it to the current DOM
|
|
||||||
diffoutputdiv.appendChild(diffview.buildView({
|
|
||||||
baseTextLines: oldLines,
|
|
||||||
newTextLines: newLines,
|
|
||||||
opcodes: opcodes,
|
|
||||||
contextSize: 4,
|
|
||||||
viewType: viewType
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function jqSelectorEscape(val) {
|
function jqSelectorEscape(val) {
|
||||||
return val.replace(/[!"#$%&'()*+,.\/:;<=>?@\[\\\]^`{|}~]/g, '\\$&');
|
return val.replace(/[!"#$%&'()*+,.\/:;<=>?@\[\\\]^`{|}~]/g, '\\$&');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function JsDiffRender(params){
|
||||||
|
var baseTextLines = (params.baseText==="")?[]:params.baseText.split(/\r\n|\r|\n/);
|
||||||
|
var headTextLines = (params.headText==="")?[]:params.newText.split(/\r\n|\r|\n/);
|
||||||
|
var sm, ctx;
|
||||||
|
if(params.ignoreSpace){
|
||||||
|
var ignoreSpace = function(a){ return a.replace(/\s+/,' ').replace(/^\s+|\s+$/,''); };
|
||||||
|
sm = new difflib.SequenceMatcher(
|
||||||
|
$.map(baseTextLines, ignoreSpace),
|
||||||
|
$.map(headTextLines, ignoreSpace));
|
||||||
|
ctx = this.flatten(sm.get_opcodes(), headTextLines, baseTextLines, function(text){ return ignoreSpace(text) === ""; });
|
||||||
|
}else{
|
||||||
|
sm = new difflib.SequenceMatcher(baseTextLines, headTextLines);
|
||||||
|
ctx = this.flatten(sm.get_opcodes(), headTextLines, baseTextLines, function(){ return false; });
|
||||||
|
}
|
||||||
|
var oplines = this.fold(ctx, params.contextSize);
|
||||||
|
|
||||||
|
function prettyDom(text){
|
||||||
|
var dom = null;
|
||||||
|
return function(ln){
|
||||||
|
if(dom===null){
|
||||||
|
dom = prettyPrintOne(text.replace(/&/g,'&').replace(/</g,'<').replace(/"/g,'"').replace(/>/g,'>'), null, true);
|
||||||
|
}
|
||||||
|
return (new RegExp('<li id="L'+ln+'"[^>]*>(.*?)</li>').exec(dom) || [])[1];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return this.renders(oplines, prettyDom(params.baseText), prettyDom(params.newText));
|
||||||
|
}
|
||||||
|
$.extend(JsDiffRender.prototype,{
|
||||||
|
renders: function(oplines, baseTextDom, headTextDom){
|
||||||
|
return {
|
||||||
|
split:function(){
|
||||||
|
var table = $('<table class="diff">');
|
||||||
|
var tbody = $('<tbody>').appendTo(table);
|
||||||
|
for(var i=0;i<oplines.length;i++){
|
||||||
|
var o = oplines[i];
|
||||||
|
switch(o.change){
|
||||||
|
case 'skip':
|
||||||
|
$('<tr>').html('<th></th><td colspan="3" class="skip">...</td>').appendTo(tbody);
|
||||||
|
break;
|
||||||
|
case 'delete':
|
||||||
|
case 'insert':
|
||||||
|
case 'equal':
|
||||||
|
$('<tr>').append(
|
||||||
|
lineNum('old',o.base),
|
||||||
|
$('<td class="body">').html(o.base ? baseTextDom(o.base): "").addClass(o.change),
|
||||||
|
lineNum('old',o.head),
|
||||||
|
$('<td class="body">').html(o.head ? headTextDom(o.head): "").addClass(o.change)
|
||||||
|
).appendTo(tbody);
|
||||||
|
break;
|
||||||
|
case 'replace':
|
||||||
|
var ld = lineDiff(baseTextDom(o.base), headTextDom(o.head));
|
||||||
|
$('<tr>').append(
|
||||||
|
lineNum('old',o.base),
|
||||||
|
$('<td class="body">').append(ld.base).addClass('delete'),
|
||||||
|
lineNum('old',o.head),
|
||||||
|
$('<td class="body">').append(ld.head).addClass('insert')
|
||||||
|
).appendTo(tbody);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return table;
|
||||||
|
},
|
||||||
|
unified:function(){
|
||||||
|
var table = $('<table class="diff inlinediff">');
|
||||||
|
var tbody = $('<tbody>').appendTo(table);
|
||||||
|
for(var i=0;i<oplines.length;i++){
|
||||||
|
var o = oplines[i];
|
||||||
|
switch(o.change){
|
||||||
|
case 'skip':
|
||||||
|
tbody.append($('<tr>').html('<th colspan="2"></th><td class="skip"></td>'));
|
||||||
|
break;
|
||||||
|
case 'delete':
|
||||||
|
case 'insert':
|
||||||
|
case 'equal':
|
||||||
|
tbody.append($('<tr>').append(
|
||||||
|
lineNum('old',o.base),
|
||||||
|
lineNum('new',o.head),
|
||||||
|
$('<td class="body">').addClass(o.change).html(o.head ? headTextDom(o.head) : baseTextDom(o.base))));
|
||||||
|
break;
|
||||||
|
case 'replace':
|
||||||
|
var deletes = [];
|
||||||
|
while(oplines[i] && oplines[i].change == 'replace'){
|
||||||
|
if(oplines[i].base && oplines[i].head){
|
||||||
|
var ld = lineDiff(baseTextDom(oplines[i].base), headTextDom(oplines[i].head));
|
||||||
|
tbody.append($('<tr>').append(lineNum('old',oplines[i].base),'<th>',$('<td class="body delete">').append(ld.base)));
|
||||||
|
deletes.push($('<tr>').append('<th>',lineNum('new',oplines[i].head),$('<td class="body insert">').append(ld.head)));
|
||||||
|
}else if(oplines[i].base){
|
||||||
|
tbody.append($('<tr>').append(lineNum('old',oplines[i].base),'<th>',$('<td class="body delete">').html(baseTextDom(oplines[i].base))));
|
||||||
|
}else if(oplines[i].head){
|
||||||
|
deletes.push($('<tr>').append('<th>',lineNum('new',oplines[i].head),$('<td class="body insert">').html(headTextDom(oplines[i].head))));
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
tbody.append(deletes);
|
||||||
|
i--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
function lineNum(type,num){
|
||||||
|
var cell = $('<th class="line-num">').addClass(type+'line');
|
||||||
|
if(num){
|
||||||
|
cell.attr('line-number',num);
|
||||||
|
}
|
||||||
|
return cell;
|
||||||
|
}
|
||||||
|
function lineDiff(b,n){
|
||||||
|
var bc = $('<diff>').html(b).children();
|
||||||
|
var nc = $('<diff>').html(n).children();
|
||||||
|
var textE = function(){ return $(this).text(); };
|
||||||
|
var sm = new difflib.SequenceMatcher(bc.map(textE), nc.map(textE));
|
||||||
|
var op = sm.get_opcodes();
|
||||||
|
if(op.length==1 || sm.ratio()<0.5){
|
||||||
|
return {base:bc,head:nc};
|
||||||
|
}
|
||||||
|
var ret = { base : [], head: []};
|
||||||
|
for(var i=0;i<op.length;i++){
|
||||||
|
var o = op[i];
|
||||||
|
switch(o[0]){
|
||||||
|
case 'equal':
|
||||||
|
ret.base=ret.base.concat(bc.slice(o[1],o[2]));
|
||||||
|
ret.head=ret.head.concat(nc.slice(o[3],o[4]));
|
||||||
|
break;
|
||||||
|
case 'delete':
|
||||||
|
case 'insert':
|
||||||
|
case 'replace':
|
||||||
|
if(o[2]!=o[1]){
|
||||||
|
ret.base.push($('<del>').append(bc.slice(o[1],o[2])));
|
||||||
|
}
|
||||||
|
if(o[4]!=o[3]){
|
||||||
|
ret.head.push($('<ins>').append(nc.slice(o[3],o[4])));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
flatten: function(opcodes, headTextLines, baseTextLines, isIgnoreLine){
|
||||||
|
var ret = [];
|
||||||
|
for (var idx = 0; idx < opcodes.length; idx++) {
|
||||||
|
var code = opcodes[idx];
|
||||||
|
var change = code[0];
|
||||||
|
var b = code[1];
|
||||||
|
var n = code[3];
|
||||||
|
var rowcnt = Math.max(code[2] - b, code[4] - n);
|
||||||
|
for (var i = 0; i < rowcnt; i++) {
|
||||||
|
switch(change){
|
||||||
|
case 'insert':
|
||||||
|
ret.push({
|
||||||
|
change:(isIgnoreLine(headTextLines[n]) ? 'equal' : change),
|
||||||
|
head: ++n
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'delete':
|
||||||
|
ret.push({
|
||||||
|
change: (isIgnoreLine(baseTextLines[b]) ? 'equal' : change),
|
||||||
|
base: ++b
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'replace':
|
||||||
|
var r = {change: change};
|
||||||
|
if(n<code[4]){
|
||||||
|
r.head = ++n;
|
||||||
|
}
|
||||||
|
if(b<code[2]){
|
||||||
|
r.base = ++b;
|
||||||
|
}
|
||||||
|
ret.push(r);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret.push({
|
||||||
|
change:change,
|
||||||
|
head: ++n,
|
||||||
|
base: ++b
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
},
|
||||||
|
fold: function(oplines, contextSize){
|
||||||
|
var ret = [], skips=[], bskip = contextSize;
|
||||||
|
for(var i=0;i<oplines.length;i++){
|
||||||
|
var o = oplines[i];
|
||||||
|
if(o.change=='equal'){
|
||||||
|
if(bskip < contextSize){
|
||||||
|
bskip ++;
|
||||||
|
ret.push(o);
|
||||||
|
}else{
|
||||||
|
skips.push(o);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
if(skips.length > contextSize){
|
||||||
|
ret.push({
|
||||||
|
change:'skip',
|
||||||
|
start:skips[0],
|
||||||
|
end:skips[skips.length-contextSize]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ret = ret.concat(skips.splice(- contextSize));
|
||||||
|
ret.push(o);
|
||||||
|
skips = [];
|
||||||
|
bskip = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(skips.length > contextSize){
|
||||||
|
ret.push({
|
||||||
|
change:'skip',
|
||||||
|
start:skips[0],
|
||||||
|
end:skips[skips.length-contextSize]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ table.diff .replace {
|
|||||||
background-color:#FD8
|
background-color:#FD8
|
||||||
}
|
}
|
||||||
table.diff .delete {
|
table.diff .delete {
|
||||||
background-color:#FFDDDD;
|
background-color:#ffecec;
|
||||||
}
|
}
|
||||||
table.diff .skip {
|
table.diff .skip {
|
||||||
background-color: #F8F8FF;
|
background-color: #F8F8FF;
|
||||||
@@ -86,10 +86,19 @@ table.diff .skip:before {
|
|||||||
content: " ...";
|
content: " ...";
|
||||||
}
|
}
|
||||||
table.diff .insert {
|
table.diff .insert {
|
||||||
background-color:#DDFFDD
|
background-color:#eaffea
|
||||||
}
|
}
|
||||||
table.diff th.author {
|
table.diff th.author {
|
||||||
text-align:right;
|
text-align:right;
|
||||||
border-top:1px solid #BBC;
|
border-top:1px solid #BBC;
|
||||||
background:#EFEFEF
|
background:#EFEFEF
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table.diff ins{
|
||||||
|
background-color: #a6f3a6;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
table.diff del{
|
||||||
|
background-color: #f8cbcb;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user