123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- /*
- * Copyright 2014 Tom <tw201207@gmail.com>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- 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;
- import org.jsoup.nodes.Element;
-
- import com.gitblit.servlet.RawServlet;
- import com.gitblit.utils.DiffUtils;
- import com.gitblit.utils.HtmlBuilder;
-
- /**
- * A {@link DiffUtils.BinaryDiffHandler BinaryDiffHandler} for images.
- *
- * @author Tom <tw201207@gmail.com>
- */
- public class ImageDiffHandler implements DiffUtils.BinaryDiffHandler {
-
- private final String oldCommitId;
- private final String newCommitId;
- private final String repositoryName;
- private final BasePage page;
- private final List<String> imageExtensions;
-
- private int imgDiffCount = 0;
-
- 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;
- this.imageExtensions = imageExtensions;
- }
-
- /** {@inheritDoc} */
- @Override
- public String renderBinaryDiff(DiffEntry diffEntry) {
- switch (diffEntry.getChangeType()) {
- case MODIFY:
- case RENAME:
- case COPY:
- // TODO: for very small images such as icons, the slider doesn't really help. Two possible
- // approaches: either upscale them for display (may show blurry upscaled images), or show
- // them side by side (may still be too small to really make out the differences).
- String oldUrl = getImageUrl(diffEntry, Side.OLD);
- String newUrl = getImageUrl(diffEntry, Side.NEW);
- if (oldUrl != null && newUrl != null) {
- imgDiffCount++;
- String id = "imgdiff" + imgDiffCount;
- HtmlBuilder builder = new HtmlBuilder("div");
- Element wrapper = builder.root().attr("class", "imgdiff-container").attr("id", "imgdiff-" + id);
- Element container = wrapper.appendElement("div").attr("class", "imgdiff-ovr-slider").appendElement("div").attr("class", "imgdiff");
- Element old = container.appendElement("div").attr("class", "imgdiff-left");
- // style='max-width:640px;' is necessary for ensuring that the browser limits large images
- // to some reasonable width, and to override the "img { max-width: 100%; }" from bootstrap.css,
- // which would scale the left image to the width of its resizeable container, which isn't what
- // we want here. Note that the max-width must be defined directly as inline style on the element,
- // otherwise browsers ignore it if the image is larger, and we end up with an image display that
- // is too wide.
- // XXX: Maybe add a max-height, too, to limit portrait-oriented images to some reasonable height?
- // (Like a 300x10000px image...)
- 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");
- 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;
- case ADD:
- String url = getImageUrl(diffEntry, Side.NEW);
- if (url != null) {
- return new HtmlBuilder("img").root().attr("class", "diff-img").attr("src", url).toString();
- }
- break;
- default:
- break;
- }
- return null;
- }
-
- /** Returns the number of image diffs generated so far by this {@link ImageDiffHandler}. */
- public int getImgDiffCount() {
- return imgDiffCount;
- }
-
- /**
- * Constructs a URL that will fetch the designated resource in the git repository. The returned string will
- * contain the URL fully URL-escaped, but note that it may still contain unescaped ampersands, so the result
- * must still be run through HTML escaping if it is to be used in HTML.
- *
- * @return the URL to the image, if the given {@link DiffEntry} and {@link Side} refers to an image, or {@code null} otherwise.
- */
- protected String getImageUrl(DiffEntry entry, Side side) {
- String path = entry.getPath(side);
- int i = path.lastIndexOf('.');
- if (i > 0) {
- String extension = path.substring(i + 1);
- for (String ext : imageExtensions) {
- if (ext.equalsIgnoreCase(extension)) {
- String commitId = Side.NEW.equals(side) ? newCommitId : oldCommitId;
- if (commitId != null) {
- return RawServlet.asLink(page.getContextUrl(), urlencode(repositoryName), commitId, urlencode(path));
- } else {
- 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
- * the %-encoding used does not encode '&' or '<'. Slashes are not encoded in the result.
- *
- * @param component
- * to encode using %-encoding
- * @return the encoded component
- */
- protected String urlencode(final String component) {
- // RawServlet handles slashes itself. Note that only the PATH_INSTANCE fits the bill here: it encodes
- // spaces as %20, and we just have to correct for encoded slashes. Java's standard URLEncoder would
- // encode spaces as '+', and I don't know what effects that would have on other parts of GitBlit. It
- // would also be wrong for path components (but fine for a query part), so we'd have to correct it, too.
- //
- // Actually, this should be done in RawServlet.asLink(). As it is now, this may be incorrect if that
- // operation ever uses query parameters instead of paths, or if it is fixed to urlencode its path
- // components. But I don't want to touch that static method in RawServlet.
- return WicketURLEncoder.PATH_INSTANCE.encode(component, StandardCharsets.UTF_8.name()).replaceAll("%2[fF]", "/");
- }
- }
|