Parcourir la source

Merged #222 "Add a blink comparator and pixel difference to image diffs"

tags/v1.7.0
James Moger il y a 9 ans
Parent
révision
57f05709b0

+ 3
- 0
src/main/java/com/gitblit/wicket/GitBlitWebApp.properties Voir le fichier

gb.diffRenamedFile = File was renamed from {0} gb.diffRenamedFile = File was renamed from {0}
gb.diffCopiedFile = File was copied from {0} gb.diffCopiedFile = File was copied from {0}
gb.diffTruncated = Diff truncated after the above file gb.diffTruncated = Diff truncated after the above file
gb.opacityAdjust = Adjust opacity
gb.blinkComparator = Blink comparator
gb.imgdiffSubtract = Subtract (black = identical)

+ 3
- 0
src/main/java/com/gitblit/wicket/GitBlitWebApp_de.properties Voir le fichier

gb.diffRenamedFile = Datei umbenannt von {0} gb.diffRenamedFile = Datei umbenannt von {0}
gb.diffCopiedFile = Datei kopiert von {0} gb.diffCopiedFile = Datei kopiert von {0}
gb.diffTruncated = Diff nach obiger Datei abgeschnitten gb.diffTruncated = Diff nach obiger Datei abgeschnitten
gb.opacityAdjust = Transparenz
gb.blinkComparator = Blinkkomparator
gb.imgdiffSubtract = Pixeldifferenz (schwarz = identisch)

+ 3
- 0
src/main/java/com/gitblit/wicket/GitBlitWebApp_fr.properties Voir le fichier

gb.diffRenamedFile = Fichier renomm\u00e9 de {0} gb.diffRenamedFile = Fichier renomm\u00e9 de {0}
gb.diffCopiedFile = Fichier copi\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.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)

+ 2
- 2
src/main/java/com/gitblit/wicket/pages/BlobDiffPage.java Voir le fichier

if (StringUtils.isEmpty(baseObjectId)) { if (StringUtils.isEmpty(baseObjectId)) {
// use first parent // use first parent
RevCommit parent = commit.getParentCount() == 0 ? null : commit.getParent(0); 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); parent.getName(), commit.getName(), imageExtensions);
diff = DiffUtils.getDiff(r, commit, blobPath, DiffOutputType.HTML, handler).content; diff = DiffUtils.getDiff(r, commit, blobPath, DiffOutputType.HTML, handler).content;
if (handler.getImgDiffCount() > 0) { if (handler.getImgDiffCount() > 0) {
} else { } else {
// base commit specified // base commit specified
RevCommit baseCommit = JGitUtils.getCommit(r, baseObjectId); RevCommit baseCommit = JGitUtils.getCommit(r, baseObjectId);
ImageDiffHandler handler = new ImageDiffHandler(getContextUrl(), repositoryName,
ImageDiffHandler handler = new ImageDiffHandler(this, repositoryName,
baseCommit.getName(), commit.getName(), imageExtensions); baseCommit.getName(), commit.getName(), imageExtensions);
diff = DiffUtils.getDiff(r, baseCommit, commit, blobPath, DiffOutputType.HTML, handler).content; diff = DiffUtils.getDiff(r, baseCommit, commit, blobPath, DiffOutputType.HTML, handler).content;
if (handler.getImgDiffCount() > 0) { if (handler.getImgDiffCount() > 0) {

+ 1
- 1
src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java Voir le fichier

add(new CommitHeaderPanel("commitHeader", repositoryName, commit)); add(new CommitHeaderPanel("commitHeader", repositoryName, commit));


final List<String> imageExtensions = app().settings().getStrings(Keys.web.imageExtensions); 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); parents.isEmpty() ? null : parents.get(0), commit.getName(), imageExtensions);
final DiffOutput diff = DiffUtils.getCommitDiff(r, commit, DiffOutputType.HTML, handler); final DiffOutput diff = DiffUtils.getCommitDiff(r, commit, DiffOutputType.HTML, handler);
if (handler.getImgDiffCount() > 0) { if (handler.getImgDiffCount() > 0) {

+ 1
- 1
src/main/java/com/gitblit/wicket/pages/ComparePage.java Voir le fichier

toCommitId.setObject(endId); toCommitId.setObject(endId);


final List<String> imageExtensions = app().settings().getStrings(Keys.web.imageExtensions); 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); fromCommit.getName(), toCommit.getName(), imageExtensions);


final DiffOutput diff = DiffUtils.getDiff(r, fromCommit, toCommit, DiffOutputType.HTML, handler); final DiffOutput diff = DiffUtils.getDiff(r, fromCommit, toCommit, DiffOutputType.HTML, handler);

+ 26
- 6
src/main/java/com/gitblit/wicket/pages/ImageDiffHandler.java Voir le fichier

import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.List; import java.util.List;


import org.apache.wicket.protocol.http.WebApplication;
import org.apache.wicket.protocol.http.WicketURLEncoder; import org.apache.wicket.protocol.http.WicketURLEncoder;
import org.eclipse.jgit.diff.DiffEntry; import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.diff.DiffEntry.Side; import org.eclipse.jgit.diff.DiffEntry.Side;
private final String oldCommitId; private final String oldCommitId;
private final String newCommitId; private final String newCommitId;
private final String repositoryName; private final String repositoryName;
private final String baseUrl;
private final BasePage page;
private final List<String> imageExtensions; private final List<String> imageExtensions;


private int imgDiffCount = 0; 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.repositoryName = repositoryName;
this.oldCommitId = oldCommitId; this.oldCommitId = oldCommitId;
this.newCommitId = newCommitId; this.newCommitId = newCommitId;
old.appendElement("img").attr("class", "imgdiff-old").attr("id", id).attr("style", "max-width:640px;").attr("src", oldUrl); 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); container.appendElement("img").attr("class", "imgdiff").attr("style", "max-width:640px;").attr("src", newUrl);
wrapper.appendElement("br"); 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(); return builder.toString();
} }
break; break;
if (ext.equalsIgnoreCase(extension)) { if (ext.equalsIgnoreCase(extension)) {
String commitId = Side.NEW.equals(side) ? newCommitId : oldCommitId; String commitId = Side.NEW.equals(side) ? newCommitId : oldCommitId;
if (commitId != null) { if (commitId != null) {
return RawServlet.asLink(baseUrl, urlencode(repositoryName), commitId, urlencode(path));
return RawServlet.asLink(page.getContextUrl(), urlencode(repositoryName), commitId, urlencode(path));
} else { } else {
return null; return null;
} }
return null; 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 * 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 '&amp;' or '&lt;'. Slashes are not encoded in the result. * the %-encoding used does not encode '&amp;' or '&lt;'. Slashes are not encoded in the result.

+ 58
- 5
src/main/java/com/gitblit/wicket/pages/scripts/imgdiff.js Voir le fichier

* *
* The styling of the slider is to be done in CSS. Currently recognized options: * The styling of the slider is to be done in CSS. Currently recognized options:
* - initial: <float> clipped to [0..1], default 0 * - 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. * If no handleClass is specified, a very plain default style is assigned.
*/ */
function rangeSlider(elem, options) { function rangeSlider(elem, options) {
options.initial = Math.min(1.0, Math.max(0, options.initial)); options.initial = Math.min(1.0, Math.max(0, options.initial));
var $elem = $(elem); 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 $root = $(document.documentElement);
var $doc = $(document); var $doc = $(document);
var lastRatio = options.initial; var lastRatio = options.initial;
var opacityAccess = rangeSlider($opacitySlider, {handleClass: 'imgdiff-opa-handle'}); var opacityAccess = rangeSlider($opacitySlider, {handleClass: 'imgdiff-opa-handle'});
var $img = $('#' + this.id.substr(this.id.indexOf('-')+1)); // Here we change opacity 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 $div = $img.parent(); // This controls visibility: here we change width.
var blinking = false;
$overlaySlider.on('slider:pos', function(e, data) { $overlaySlider.on('slider:pos', function(e, data) {
var pos = $(data.handle).offset().left; var pos = $(data.handle).offset().left;
} }
}); });
$opacitySlider.on('slider:pos', function(e, data) { $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); $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 newRatio = (e.pageX - $opacitySlider.offset().left) / $opacitySlider.innerWidth();
var oldRatio = opacityAccess.getRatio(); var oldRatio = opacityAccess.getRatio();
if (newRatio !== oldRatio) { if (newRatio !== oldRatio) {
e.preventDefault(); 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();
});
}
}); });
} }



BIN
src/main/resources/blink32.png Voir le fichier


+ 18
- 1
src/main/resources/gitblit.css Voir le fichier

user-select: none; user-select: none;
border: 1px solid #F00; border: 1px solid #F00;
} }
.imgdiff-opa-container { .imgdiff-opa-container {
display: inline-block;
width: 200px; width: 200px;
height: 4px; height: 4px;
margin: 12px 35px;
margin: 12px 35px 6px 35px;
padding: 0; padding: 0;
position: relative; position: relative;
border: 1px solid #888; border: 1px solid #888;
} }
.imgdiff-opa-handle { .imgdiff-opa-handle {
display: inline-block;
width: 10px; width: 10px;
height: 10px; height: 10px;
position: absolute; position: absolute;
} }
.imgdiff-ovr-handle { .imgdiff-ovr-handle {
display: inline-block;
width : 1px; width : 1px;
height: 100%; height: 100%;
top: 0px; top: 0px;
/* With CSS: background-image: radial-gradient(5px at 50% 50%, #444, #888, transparent 5px); */ /* 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 */ /* End image diffs */
td.changeType { td.changeType {

BIN
src/main/resources/sub32.png Voir le fichier


Chargement…
Annuler
Enregistrer