@@ -757,3 +757,6 @@ gb.diffDeletedFile = File was deleted | |||
gb.diffRenamedFile = File was renamed from {0} | |||
gb.diffCopiedFile = File was copied from {0} | |||
gb.diffTruncated = Diff truncated after the above file | |||
gb.opacityAdjust = Adjust opacity | |||
gb.blinkComparator = Blink comparator | |||
gb.imgdiffSubtract = Subtract (black = identical) |
@@ -750,3 +750,6 @@ gb.diffDeletedFile = Datei wurde gel\u00f6scht | |||
gb.diffRenamedFile = Datei umbenannt von {0} | |||
gb.diffCopiedFile = Datei kopiert von {0} | |||
gb.diffTruncated = Diff nach obiger Datei abgeschnitten | |||
gb.opacityAdjust = Transparenz | |||
gb.blinkComparator = Blinkkomparator | |||
gb.imgdiffSubtract = Pixeldifferenz (schwarz = identisch) |
@@ -679,3 +679,6 @@ gb.diffDeletedFile = Fichier a \u00e9t\u00e9 effac\u00e9 | |||
gb.diffRenamedFile = Fichier renomm\u00e9 de {0} | |||
gb.diffCopiedFile = Fichier copi\u00e9 de {0} | |||
gb.diffTruncated = Affichage de diff\u00e9rences supprim\u00e9e apr\u00e8s le fichier ci-dessus | |||
gb.opacityAdjust = ajuster l'opacit\u00e9 | |||
gb.blinkComparator = Comparateur \u00e0 clignotement | |||
gb.imgdiffSubtract = Diff\u00e9rence (noir = identique) |
@@ -52,7 +52,7 @@ public class BlobDiffPage extends RepositoryPage { | |||
if (StringUtils.isEmpty(baseObjectId)) { | |||
// use first parent | |||
RevCommit parent = commit.getParentCount() == 0 ? null : commit.getParent(0); | |||
ImageDiffHandler handler = new ImageDiffHandler(getContextUrl(), repositoryName, | |||
ImageDiffHandler handler = new ImageDiffHandler(this, repositoryName, | |||
parent.getName(), commit.getName(), imageExtensions); | |||
diff = DiffUtils.getDiff(r, commit, blobPath, DiffOutputType.HTML, handler).content; | |||
if (handler.getImgDiffCount() > 0) { | |||
@@ -63,7 +63,7 @@ public class BlobDiffPage extends RepositoryPage { | |||
} else { | |||
// base commit specified | |||
RevCommit baseCommit = JGitUtils.getCommit(r, baseObjectId); | |||
ImageDiffHandler handler = new ImageDiffHandler(getContextUrl(), repositoryName, | |||
ImageDiffHandler handler = new ImageDiffHandler(this, repositoryName, | |||
baseCommit.getName(), commit.getName(), imageExtensions); | |||
diff = DiffUtils.getDiff(r, baseCommit, commit, blobPath, DiffOutputType.HTML, handler).content; | |||
if (handler.getImgDiffCount() > 0) { |
@@ -82,7 +82,7 @@ public class CommitDiffPage extends RepositoryPage { | |||
add(new CommitHeaderPanel("commitHeader", repositoryName, commit)); | |||
final List<String> imageExtensions = app().settings().getStrings(Keys.web.imageExtensions); | |||
final ImageDiffHandler handler = new ImageDiffHandler(getContextUrl(), repositoryName, | |||
final ImageDiffHandler handler = new ImageDiffHandler(this, repositoryName, | |||
parents.isEmpty() ? null : parents.get(0), commit.getName(), imageExtensions); | |||
final DiffOutput diff = DiffUtils.getCommitDiff(r, commit, DiffOutputType.HTML, handler); | |||
if (handler.getImgDiffCount() > 0) { |
@@ -113,7 +113,7 @@ public class ComparePage extends RepositoryPage { | |||
toCommitId.setObject(endId); | |||
final List<String> imageExtensions = app().settings().getStrings(Keys.web.imageExtensions); | |||
final ImageDiffHandler handler = new ImageDiffHandler(getContextUrl(), repositoryName, | |||
final ImageDiffHandler handler = new ImageDiffHandler(this, repositoryName, | |||
fromCommit.getName(), toCommit.getName(), imageExtensions); | |||
final DiffOutput diff = DiffUtils.getDiff(r, fromCommit, toCommit, DiffOutputType.HTML, handler); |
@@ -18,6 +18,7 @@ package com.gitblit.wicket.pages; | |||
import java.nio.charset.StandardCharsets; | |||
import java.util.List; | |||
import org.apache.wicket.protocol.http.WebApplication; | |||
import org.apache.wicket.protocol.http.WicketURLEncoder; | |||
import org.eclipse.jgit.diff.DiffEntry; | |||
import org.eclipse.jgit.diff.DiffEntry.Side; | |||
@@ -37,14 +38,14 @@ public class ImageDiffHandler implements DiffUtils.BinaryDiffHandler { | |||
private final String oldCommitId; | |||
private final String newCommitId; | |||
private final String repositoryName; | |||
private final String baseUrl; | |||
private final BasePage page; | |||
private final List<String> imageExtensions; | |||
private int imgDiffCount = 0; | |||
public ImageDiffHandler(final String baseUrl, final String repositoryName, final String oldCommitId, | |||
final String newCommitId, final List<String> imageExtensions) { | |||
this.baseUrl = baseUrl; | |||
public ImageDiffHandler(final BasePage page, final String repositoryName, final String oldCommitId, final String newCommitId, | |||
final List<String> imageExtensions) { | |||
this.page = page; | |||
this.repositoryName = repositoryName; | |||
this.oldCommitId = oldCommitId; | |||
this.newCommitId = newCommitId; | |||
@@ -81,7 +82,19 @@ public class ImageDiffHandler implements DiffUtils.BinaryDiffHandler { | |||
old.appendElement("img").attr("class", "imgdiff-old").attr("id", id).attr("style", "max-width:640px;").attr("src", oldUrl); | |||
container.appendElement("img").attr("class", "imgdiff").attr("style", "max-width:640px;").attr("src", newUrl); | |||
wrapper.appendElement("br"); | |||
wrapper.appendElement("div").attr("class", "imgdiff-opa-container").appendElement("div").attr("class", "imgdiff-opa-slider"); | |||
Element controls = wrapper.appendElement("div"); | |||
// Opacity slider | |||
controls.appendElement("div").attr("class", "imgdiff-opa-container").appendElement("a").attr("class", "imgdiff-opa-slider") | |||
.attr("href", "#").attr("title", page.getString("gb.opacityAdjust")); | |||
// Blink comparator: find Pluto! | |||
controls.appendElement("a").attr("class", "imgdiff-link imgdiff-blink").attr("href", "#") | |||
.attr("title", page.getString("gb.blinkComparator")) | |||
.appendElement("img").attr("src", getStaticResourceUrl("blink32.png")).attr("width", "20"); | |||
// Pixel subtraction, initially not displayed, will be shown by imgdiff.js depending on feature test. | |||
// (Uses CSS mix-blend-mode, which isn't supported on all browsers yet). | |||
controls.appendElement("a").attr("class", "imgdiff-link imgdiff-subtract").attr("href", "#") | |||
.attr("title", page.getString("gb.imgdiffSubtract")).attr("style", "display:none;") | |||
.appendElement("img").attr("src", getStaticResourceUrl("sub32.png")).attr("width", "20"); | |||
return builder.toString(); | |||
} | |||
break; | |||
@@ -118,7 +131,7 @@ public class ImageDiffHandler implements DiffUtils.BinaryDiffHandler { | |||
if (ext.equalsIgnoreCase(extension)) { | |||
String commitId = Side.NEW.equals(side) ? newCommitId : oldCommitId; | |||
if (commitId != null) { | |||
return RawServlet.asLink(baseUrl, urlencode(repositoryName), commitId, urlencode(path)); | |||
return RawServlet.asLink(page.getContextUrl(), urlencode(repositoryName), commitId, urlencode(path)); | |||
} else { | |||
return null; | |||
} | |||
@@ -128,6 +141,13 @@ public class ImageDiffHandler implements DiffUtils.BinaryDiffHandler { | |||
return null; | |||
} | |||
/** | |||
* Returns a URL that will fetch the designated static resource from within GitBlit. | |||
*/ | |||
protected String getStaticResourceUrl(String contextRelativePath) { | |||
return WebApplication.get().getRequestCycleProcessor().getRequestCodingStrategy().rewriteStaticRelativeUrl(contextRelativePath); | |||
} | |||
/** | |||
* Encode a URL component of a {@link RawServlet} URL in the special way that the servlet expects it. Note that | |||
* the %-encoding used does not encode '&' or '<'. Slashes are not encoded in the result. |
@@ -22,7 +22,7 @@ | |||
* | |||
* The styling of the slider is to be done in CSS. Currently recognized options: | |||
* - initial: <float> clipped to [0..1], default 0 | |||
* - handleClass: <string> to assign to the handle div element created. | |||
* - handleClass: <string> to assign to the handle span element created. | |||
* If no handleClass is specified, a very plain default style is assigned. | |||
*/ | |||
function rangeSlider(elem, options) { | |||
@@ -30,7 +30,7 @@ function rangeSlider(elem, options) { | |||
options.initial = Math.min(1.0, Math.max(0, options.initial)); | |||
var $elem = $(elem); | |||
var $handle = $('<div></div>').css({ position: 'absolute', left: 0, cursor: 'ew-resize' }); | |||
var $handle = $('<span></span>').css({ position: 'absolute', left: 0, cursor: 'ew-resize' }); | |||
var $root = $(document.documentElement); | |||
var $doc = $(document); | |||
var lastRatio = options.initial; | |||
@@ -144,6 +144,7 @@ function setup() { | |||
var opacityAccess = rangeSlider($opacitySlider, {handleClass: 'imgdiff-opa-handle'}); | |||
var $img = $('#' + this.id.substr(this.id.indexOf('-')+1)); // Here we change opacity | |||
var $div = $img.parent(); // This controls visibility: here we change width. | |||
var blinking = false; | |||
$overlaySlider.on('slider:pos', function(e, data) { | |||
var pos = $(data.handle).offset().left; | |||
@@ -167,11 +168,10 @@ function setup() { | |||
} | |||
}); | |||
$opacitySlider.on('slider:pos', function(e, data) { | |||
if ($div.width() <= 0) overlayAccess.moveAuto(1.0); // Make old image visible in a nice way | |||
if ($div.width() <= 0 && !blinking) overlayAccess.moveAuto(1.0); // Make old image visible in a nice way | |||
$img.css('opacity', 1.0 - data.ratio); | |||
}); | |||
$opacitySlider.css('cursor', 'pointer'); | |||
$opacitySlider.on('mousedown', function(e) { | |||
$opacitySlider.on('click', function(e) { | |||
var newRatio = (e.pageX - $opacitySlider.offset().left) / $opacitySlider.innerWidth(); | |||
var oldRatio = opacityAccess.getRatio(); | |||
if (newRatio !== oldRatio) { | |||
@@ -184,6 +184,59 @@ function setup() { | |||
e.preventDefault(); | |||
}); | |||
// Blinking before and after images is a good way for the human eye to catch differences. | |||
var $blinker = $this.find('.imgdiff-blink'); | |||
var initialOpacity = null; | |||
$blinker.on('click', function(e) { | |||
if (blinking) { | |||
window.clearTimeout(blinking); | |||
$blinker.children('img').first().css('border', '1px solid transparent'); | |||
opacityAccess.setRatio(initialOpacity); | |||
blinking = null; | |||
} else { | |||
$blinker.children('img').first().css('border', '1px solid #AAA'); | |||
initialOpacity = opacityAccess.getRatio(); | |||
var currentOpacity = 1.0; | |||
function blink() { | |||
opacityAccess.setRatio(currentOpacity); | |||
currentOpacity = 1.0 - currentOpacity; | |||
// Keep frequeny below 2Hz (i.e., delay above 500ms) | |||
blinking = window.setTimeout(blink, 600); | |||
} | |||
if ($div.width() <= 0) { | |||
overlayAccess.moveRatio(1.0, 500, blink); | |||
} else { | |||
blink(); | |||
} | |||
} | |||
e.preventDefault(); | |||
}); | |||
// Subtracting before and after images is another good way to detect differences. Result will be | |||
// black where identical. | |||
if (typeof $img[0].style.mixBlendMode != 'undefined') { | |||
// Feature test: does the browser support the mix-blend-mode CSS property from the Compositing | |||
// and Blending Level 1 spec (http://dev.w3.org/fxtf/compositing-1/#mix-blend-mode )? | |||
// As of 2014-11, only Firefox >= 32 and Safari >= 7.1 support this. Other browsers will have to | |||
// make do with the blink comparator only. | |||
var $sub = $this.find('.imgdiff-subtract'); | |||
$sub.css('display', 'inline-block'); | |||
$sub.on('click', function (e) { | |||
var curr = $img.css('mix-blend-mode'); | |||
if (curr != 'difference') { | |||
curr = 'difference'; | |||
$sub.children('img').first().css('border', '1px solid #AAA'); | |||
if ($div.width() <= 0) overlayAccess.moveRatio(1.0, 500); | |||
opacityAccess.setRatio(0); | |||
} else { | |||
curr = 'normal'; | |||
$sub.children('img').first().css('border', '1px solid transparent'); | |||
} | |||
$img.css('mix-blend-mode', curr); | |||
e.preventDefault(); | |||
}); | |||
} | |||
}); | |||
} | |||
@@ -1490,10 +1490,12 @@ img.imgdiff-old { | |||
user-select: none; | |||
border: 1px solid #F00; | |||
} | |||
.imgdiff-opa-container { | |||
display: inline-block; | |||
width: 200px; | |||
height: 4px; | |||
margin: 12px 35px; | |||
margin: 12px 35px 6px 35px; | |||
padding: 0; | |||
position: relative; | |||
border: 1px solid #888; | |||
@@ -1532,6 +1534,7 @@ img.imgdiff-old { | |||
} | |||
.imgdiff-opa-handle { | |||
display: inline-block; | |||
width: 10px; | |||
height: 10px; | |||
position: absolute; | |||
@@ -1549,6 +1552,7 @@ img.imgdiff-old { | |||
} | |||
.imgdiff-ovr-handle { | |||
display: inline-block; | |||
width : 1px; | |||
height: 100%; | |||
top: 0px; | |||
@@ -1578,6 +1582,19 @@ img.imgdiff-old { | |||
/* With CSS: background-image: radial-gradient(5px at 50% 50%, #444, #888, transparent 5px); */ | |||
} | |||
.imgdiff-link { | |||
margin: 0px 4px; | |||
text-decoration: none; | |||
border: none; | |||
} | |||
.imgdiff-link > img { | |||
border: 1px solid transparent; /* Avoid jumping when we change the border */ | |||
width: 20px; | |||
height: 20px; | |||
margin-bottom: 10px; | |||
} | |||
/* End image diffs */ | |||
td.changeType { |