Merged #222 "Add a blink comparator and pixel difference to image diffs"
2 files added
9 files modified
| | |
| | | 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) |
| | |
| | | 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) |
| | |
| | | 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) |
| | |
| | | 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) {
|
| | |
| | | } 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) {
|
| | |
| | | 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) { |
| | |
| | | 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); |
| | |
| | | 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; |
| | |
| | | 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; |
| | |
| | | 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; |
| | |
| | | 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; |
| | | } |
| | |
| | | } |
| | | |
| | | /** |
| | | * 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. |
| | | * |
| | |
| | | * |
| | | * 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) { |
| | |
| | | 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; |
| | |
| | | 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; |
| | |
| | | } |
| | | }); |
| | | $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) { |
| | |
| | | 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(); |
| | | }); |
| | | } |
| | | }); |
| | | } |
| | | |
| | |
| | | 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;
|
| | |
| | | }
|
| | |
|
| | | .imgdiff-opa-handle {
|
| | | display: inline-block;
|
| | | width: 10px;
|
| | | height: 10px;
|
| | | position: absolute;
|
| | |
| | | }
|
| | |
|
| | | .imgdiff-ovr-handle {
|
| | | display: inline-block;
|
| | | width : 1px;
|
| | | height: 100%;
|
| | | top: 0px;
|
| | |
| | | /* 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 {
|