package org.vaadin.jcrop; import java.io.IOException; import java.net.URI; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.UUID; import org.vaadin.jcrop.client.JcropServerRpc; import org.vaadin.jcrop.client.JcropState; import org.vaadin.jcrop.selection.JcropSelection; import org.vaadin.jcrop.selection.JcropSelectionChanged; import com.vaadin.annotations.JavaScript; import com.vaadin.annotations.StyleSheet; import com.vaadin.server.ConnectorResource; import com.vaadin.server.Page; import com.vaadin.server.RequestHandler; import com.vaadin.server.VaadinRequest; import com.vaadin.server.VaadinResponse; import com.vaadin.server.VaadinSession; import com.vaadin.ui.AbstractJavaScriptComponent; /** * Vaadin wrapper for the jQuery Plugin Jcrop(https://github.com/tapmodo/Jcrop)<br> * * @author Marten Prieß (http://www.non-rocket-science.com) * @version 1.0 */ @StyleSheet("vaadin://jcrop/jquery.Jcrop.min.css") @JavaScript({ "vaadin://jcrop/jquery-1.11.2.min.js", "vaadin://jcrop/jquery.Jcrop.min.js", "Jcrop.js" }) public class Jcrop extends AbstractJavaScriptComponent { private final List<JcropSelectionChanged> listeners = new ArrayList<>(); private JcropSelection selection = null; private ConnectorResource resource; private String requestHandlerUri = null; public Jcrop() { /** * register the ServerRpc to the server-implementation in order to listen to it's calls */ registerRpc(new JcropServerRpc() { @Override public void cropSelectionChanged(final Integer x, final Integer y, final Integer width, final Integer height) { if (Jcrop.this.isEnabled()) { // update the state values setSelection(x, y, width, height, false); // trigger listeners for (JcropSelectionChanged listener : Jcrop.this.listeners) { // selection will get updated within setSelection listener.selectionChanged(Jcrop.this.selection); } } } }); setPrimaryStyleName("image-cropper"); } /** * will release the selection */ public void clearSelection() { getState().x = 0; getState().y = 0; getState().cropWidth = 0; getState().cropHeight = 0; callFunction("clearSelection"); } /** * change the base url of image. this will reinitialize the Jcrop. * * @param imageUrl */ public void setImageUrl(final String imageUrl) { getState().imageUrl = imageUrl; triggerRepaint(); } /** * will initialize a RequestHandler and set the imageUrl to the handler * * @param resource */ public void setResource(final ConnectorResource resource) { initRequestHandler(); this.resource = resource; setImageUrl(getRequestUri()); } /** * initalize a unique RequestHandler */ private void initRequestHandler() { if (this.requestHandlerUri == null) { this.requestHandlerUri = UUID.randomUUID() .toString(); VaadinSession.getCurrent() .addRequestHandler(new RequestHandler() { @Override public boolean handleRequest(final VaadinSession session, final VaadinRequest request, final VaadinResponse response) throws IOException { if (String.format("/%s", Jcrop.this.requestHandlerUri) .equals(request.getPathInfo())) { Jcrop.this.resource.getStream() .writeResponse(request, response); return true; } else { return false; } } }); } } /** * generate's the requestUri for the RequestHandler with a ts-parameter to avoid caching of browser on resource-changes * * @return */ private String getRequestUri() { URI uri = Page.getCurrent() .getLocation(); String url = null; if ((uri.getPort() == 80) || (uri.getPort() == 443) || (uri.getPort() <= 0)) { url = uri.getScheme() + "://" + uri.getHost() + uri.getPath(); } else { url = uri.getScheme() + "://" + uri.getHost() + ":" + uri.getPort() + uri.getPath(); } if (url != null && !url.endsWith("/")) { url += "/"; } url += this.requestHandlerUri + "?ts=" + new Date().getTime(); return url; } /** * get the current selection state * * @return could be null */ public JcropSelection getSelection() { return this.selection; } /** * set the selection of Jcrop programmatically<br> * is only working when the component is already attached to the screen * * @param x * @param y * @param width * @param height */ public void setSelection(final int x, final int y, final int width, final int height) { setSelection(x, y, width, height, true); } /** * set the selection of Jcrop programmatically<br> * is only working when the component is already attached to the screen * * @param selection */ public void setSelection(final JcropSelection selection) { setSelection(selection.getX(), selection.getY(), selection.getWidth(), selection.getHeight(), true); } private void setSelection(final int x, final int y, final int width, final int height, final boolean triggerSelection) { getState().x = x; getState().y = y; getState().cropWidth = width; getState().cropHeight = height; this.selection = new JcropSelection(x, y, width, height, width <= 0 && height <= 0); if (triggerSelection) { callFunction("setSelection", x, y, width, height); } } /** * set the aspectRatio of the selection<br> * Aspect ratio of w/h (e.g. 1 for square)<br> * by default it's 0 and so disabled * * @param aspectRatio */ public void setAspectRatio(final float aspectRatio) { if (getState().aspectRatio != aspectRatio) { getState().aspectRatio = aspectRatio; triggerRepaint(); } } /** * set the minimum size of the crop selection<br> * Minimum width/height, use 0 for unbounded dimension<br> * by default it's unbounded * * @param x * @param y */ public void setMinCropSize(final int x, final int y) { if (getState().minSizeX != x || getState().minSizeY != y) { getState().minSizeX = x; getState().minSizeY = y; triggerRepaint(); } } /** * set the maximum size of the crop selection<br> * Minimum width/height, use 0 for unbounded dimension<br> * by default it's unbounded * * @param x * @param y */ public void setMaxCropSize(final int x, final int y) { if (getState().maxSizeX != x || getState().maxSizeY != y) { getState().maxSizeX = x; getState().maxSizeY = y; triggerRepaint(); } } /** * add a selection on the selectionChanged event * * @param listener */ public void addListener(final JcropSelectionChanged listener) { this.listeners.add(listener); } /** * remove a selection of the selectionChanged event * * @param listener */ public void removeListener(final JcropSelectionChanged listener) { this.listeners.remove(listener); } /** * explicitly map the custom state object to the server implementation * * @return */ @Override protected JcropState getState() { return (JcropState) super.getState(); } /** * used only to reinitialize the jCrop component when needed */ private void triggerRepaint() { this.getState().triggerRepaint = String.valueOf(new Date().getTime()); } }