--- /dev/null
+package com.google.gwt.query.client.plugins;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.NodeList;
+import com.google.gwt.event.shared.GwtEvent;
+import com.google.gwt.event.shared.HasHandlers;
+import com.google.gwt.query.client.Function;
+import com.google.gwt.query.client.GQuery;
+import com.google.gwt.query.client.JSArray;
+import com.google.gwt.query.client.Plugin;
+import com.google.gwt.query.client.Predicate;
+
+/**
+ * GWT clone of jQueryUi-core
+ *
+ * @author Julien Dramaix (julien.dramaix@gmail.com, @jdramaix)
+ *
+ */
+public class GQueryUi extends GQuery {
+
+ /**
+ * This object allows you to have a full copy of the original Event and
+ * implements some useful method of the jQuery event model. This is also
+ * useful in Internet Explorer because it use the same javascript object to
+ * fire MouseDownEvent, MouseMoveEvent or MouseStopEvent on the same element.
+ * So, we cannot keep a copy of the MouseDownEvent during a dragginf for
+ * example. Now we can !
+ *
+ * TOBEFIXED : the method preventDefault() must be called directly on the
+ * original event
+ *
+ *
+ * @author jdramaix
+ *
+ */
+ public static class Event extends com.google.gwt.user.client.Event {
+
+ public static Event create(com.google.gwt.user.client.Event originalEvent) {
+ Event gQueryEvent = createObject().cast();
+ copy(originalEvent, gQueryEvent);
+ return gQueryEvent;
+ }
+
+ private static native void copy(
+ com.google.gwt.user.client.Event originalEvent, Event gQueryEvent) /*-{
+ for ( var field in originalEvent ) {
+ gQueryEvent[field] = originalEvent[field];
+ }
+ gQueryEvent.originalEvent = originalEvent;
+ }-*/;
+
+ protected Event() {
+ }
+
+ public final native com.google.gwt.user.client.Event getOriginalEvent()/*-{
+ return this.originalEvent;
+ }-*/;
+
+ /**
+ * The mouse position relative to the left edge of the document
+ *
+ * @param mouseEvent
+ * @return the mouse x-position in the page
+ */
+ public final int pageX() {
+ return getClientX() + document.getScrollLeft();
+ }
+
+ /**
+ * The mouse position relative to the top edge of the document.
+ *
+ * @param mouseEvent
+ * @return the mouse y-position in the page
+ */
+ public final int pageY() {
+ return getClientY() + document.getScrollTop();
+ }
+
+ /**
+ * Tell if ctrl or cmd key is pressed
+ *
+ * @return
+ */
+ public final boolean isMetaKeyPressed() {
+ return getMetaKey() || getCtrlKey();
+ }
+
+ }
+
+ public static class Dimension {
+ private int width = 0;
+ private int height = 0;
+
+ public Dimension(Element e) {
+ width = e.getOffsetWidth();
+ height = e.getOffsetHeight();
+ }
+
+ public Dimension(int width, int height) {
+ this.width = width;
+ this.height = height;
+ }
+
+ public int getHeight() {
+ return height;
+ }
+
+ public int getWidth() {
+ return width;
+ }
+ }
+
+ private static class GQueryUiImpl {
+
+ private final Element getViewportElement() {
+ return GQuery.document.isCSS1Compat() ? GQuery.document
+ .getDocumentElement() : GQuery.document.getBody();
+ }
+
+ public GQuery scrollParent(final GQueryUi gQueryUi) {
+ GQuery scrollParent;
+
+ if ("fixed".equals(gQueryUi.css("position"))) {
+ return GQuery.$(getViewportElement());
+ }
+
+ if (scrollParentPositionTest(gQueryUi)) {
+ scrollParent = gQueryUi.parents().filter(new Predicate() {
+
+ public boolean f(Element e, int index) {
+ GQuery $e = GQuery.$(e);
+ String position = $e.css("position", true);
+ return ("relative".equals(position) || "absolute".equals(position) || "fixed"
+ .equals(position))
+ && isOverflowEnabled($e);
+ }
+ });
+
+ } else {
+ scrollParent = gQueryUi.parents().filter(new Predicate() {
+
+ public boolean f(Element e, int index) {
+ return isOverflowEnabled(GQuery.$(e));
+ }
+ });
+ }
+ return scrollParent.length() > 0 ? new GQuery(scrollParent.get(0))
+ : GQuery.$(getViewportElement());
+
+ }
+
+ private boolean isOverflowEnabled(GQuery e) {
+ String overflow = e.css("overflow", true) + e.css("overflow-x", true)
+ + e.css("overflow-y", true);
+ return overflow.contains("auto") || overflow.contains("scroll");
+ }
+
+ protected boolean scrollParentPositionTest(GQueryUi gQueryUi) {
+ return "absolute".equals(gQueryUi.css("position"));
+ }
+
+ }
+
+ @SuppressWarnings("unused")
+ private static class GQueryUiImplTrident extends GQueryUiImpl {
+
+ @Override
+ protected boolean scrollParentPositionTest(GQueryUi gQueryUi) {
+ String position = gQueryUi.css("position");
+ return ("absolute".equals(position) || "relative".equals(position) || "static"
+ .equals(position));
+ }
+
+ }
+
+ public static Class<GQueryUi> GQueryUi = GQueryUi.class;
+
+ // Register the plugin in GQuery
+ static {
+ GQuery.registerPlugin(GQueryUi.class, new Plugin<GQueryUi>() {
+ public GQueryUi init(GQuery gq) {
+ return new GQueryUi(gq);
+ }
+ });
+ }
+
+ protected HasHandlers eventBus;
+
+ private GQueryUiImpl impl = GWT.create(GQueryUiImpl.class);
+
+ public static boolean contains(Element parent, Element descendant) {
+ return parent != descendant && parent.isOrHasChild(descendant);
+ }
+
+ public GQueryUi() {
+ super();
+ }
+
+ public GQueryUi(Element element) {
+ super(element);
+ }
+
+ public GQueryUi(GQuery gq) {
+ super(gq);
+ }
+
+ public GQueryUi(JSArray elements) {
+ super(elements);
+ }
+
+ public GQueryUi(NodeList<Element> list) {
+ super(list);
+ }
+
+ /**
+ * this function returns the immediate scrolling parent.
+ *
+ * @return the immediate scrolling parent
+ */
+ public GQuery scrollParent() {
+ return impl.scrollParent(this);
+ }
+
+ /**
+ * fire event and call callback function.
+ *
+ * @param e
+ * @param callback
+ * @param element
+ */
+ protected void trigger(GwtEvent<?> e, Function callback, Element element) {
+ trigger(e, callback, element, eventBus);
+ }
+
+ protected static void trigger(GwtEvent<?> e, Function callback,
+ Element element, HasHandlers handlerManager) {
+ if (handlerManager != null && e != null) {
+ handlerManager.fireEvent(e);
+ }
+ if (callback != null) {
+ callback.f(element);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Copyright 2010 The gwtquery plugins team.
+ *
+ * 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.google.gwt.query.client.plugins;
+
+public class MouseOptions {
+
+ /**
+ * Prevents plugin working from starting on specified elements.
+ */
+ private String[] cancel;
+
+ /**
+ * Time in milliseconds to define when the plugin should start. It helps
+ * preventing unwanted selections when clicking on an element. Default : 0
+ *
+ */
+ private int delay;
+
+ /**
+ * Tolerance, in pixels, for when plugin should start. If specified, selecting
+ * will not start until after mouse is dragged beyond distance. Default : 1
+ *
+ */
+ private int distance;
+
+ public MouseOptions() {
+ initDefault();
+ }
+
+ public String[] getCancel() {
+ return cancel;
+ }
+
+ public int getDelay() {
+ return delay;
+ }
+
+ public int getDistance() {
+ return distance;
+ }
+
+ /**
+ * Prevents starting of the plugin on specified elements
+ * @param cancel array of css selectors
+ */
+ public void setCancel(String... cancel) {
+ this.cancel = cancel;
+ }
+
+ /**
+ * Time in milliseconds to define when the plugin should start. It helps
+ * preventing unwanted selections when clicking on an element.
+ * @param delay
+ */
+ public void setDelay(int delay) {
+ this.delay = delay;
+ }
+
+ /**
+ * Tolerance, in pixels, for when plugin should start. If specified, selecting
+ * will not start until after mouse is dragged beyond distance. Default : 1
+ * @param distance
+ */
+ public void setDistance(int distance) {
+ this.distance = distance;
+ }
+
+ protected void initDefault() {
+ delay = 0;
+ distance = 1; //by default, the mouse have to move one pixel !
+ cancel = new String[]{"input", "option"};
+ }
+}
+
--- /dev/null
+/*\r
+ * Copyright 2010 The gwtquery plugins team.\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not\r
+ * use this file except in compliance with the License. You may obtain a copy of\r
+ * the License at\r
+ * \r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\r
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\r
+ * License for the specific language governing permissions and limitations under\r
+ * the License.\r
+ */\r
+package com.google.gwt.query.client.plugins;\r
+\r
+import com.google.gwt.core.client.Duration;\r
+import com.google.gwt.dom.client.Element;\r
+import com.google.gwt.dom.client.NativeEvent;\r
+import com.google.gwt.dom.client.NodeList;\r
+import com.google.gwt.query.client.Function;\r
+import com.google.gwt.query.client.GQuery;\r
+import com.google.gwt.query.client.JSArray;\r
+import com.google.gwt.query.client.plugins.Events;\r
+\r
+/**\r
+ * Base class for all plug-in that need to handle some mouse interactions.\r
+ * \r
+ * @author Julien Dramaix (julien.dramaix@gmail.com, @jdramaix)\r
+ * \r
+ */\r
+public abstract class MousePlugin extends GQueryUi {\r
+\r
+ private Event mouseDownEvent;\r
+ private boolean mouseStarted = false;\r
+ private Duration mouseUpDuration;\r
+ // private int mouseDownX;\r
+ // private int mouseDownY;\r
+ private MouseOptions options;\r
+ private boolean preventClickEvent = false;\r
+\r
+ public MousePlugin(Element element) {\r
+ super(element);\r
+ }\r
+\r
+ public MousePlugin(GQuery gq) {\r
+ super(gq);\r
+ }\r
+\r
+ public MousePlugin(JSArray elements) {\r
+ super(elements);\r
+ }\r
+\r
+ public MousePlugin(NodeList<Element> list) {\r
+ super(list);\r
+ }\r
+\r
+ protected void destroyMouseHandler() {\r
+ as(Events.Events)\r
+ .unbind(Event.ONMOUSEDOWN | Event.ONCLICK, getPluginName());\r
+ }\r
+\r
+ /**\r
+ * Return a String identifying the plugin. This string is used as namespace\r
+ * when we bind handlers.\r
+ * \r
+ * @return\r
+ */\r
+ protected abstract String getPluginName();\r
+\r
+ /**\r
+ * This method initialize all needed handlers\r
+ * \r
+ * @param options\r
+ */\r
+ protected void initMouseHandler(MouseOptions options) {\r
+ this.options = options;\r
+\r
+ for (final Element e : elements()) {\r
+\r
+ $(e).as(Events.Events).bind(Event.ONMOUSEDOWN, getPluginName(),\r
+ (Object) null, new Function() {\r
+ @Override\r
+ public boolean f(com.google.gwt.user.client.Event event) {\r
+ return mouseDown(e, Event.create(event));\r
+\r
+ }\r
+ }).bind(Event.ONCLICK, getPluginName(), (Object) null,\r
+ new Function() {\r
+ @Override\r
+ public boolean f(com.google.gwt.user.client.Event event) {\r
+ preventClickEvent |= !mouseClick(e, Event.create(event));\r
+\r
+ if (preventClickEvent) {\r
+\r
+ preventClickEvent = false;\r
+ event.stopPropagation();\r
+ event.preventDefault();\r
+ return false;\r
+ }\r
+\r
+ return true;\r
+ }\r
+ });\r
+ }\r
+\r
+ }\r
+\r
+ /**\r
+ * Test if the mouse down event must be handled by the plugin or not.\r
+ * \r
+ * \r
+ */\r
+ protected boolean mouseCapture(Element draggable, Event event) {\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * Method called when mouse click\r
+ * \r
+ * @param element\r
+ * @param event\r
+ * @return\r
+ */\r
+ protected boolean mouseClick(Element element, Event event) {\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * Method called when mouse down occur on the element.\r
+ * \r
+ * You should not override this method. Instead, override\r
+ * {@link #mouseStart(Element, Event)} method\r
+ * \r
+ * @param element\r
+ * @param event\r
+ * @return\r
+ */\r
+ protected boolean mouseDown(Element element, Event event) {\r
+\r
+ // test if an other plugin handle the mouseStart\r
+ if (isEventAlreadyHandled(event)) {\r
+ return false;\r
+ }\r
+ if (mouseStarted) { // case where we missed a mouseup\r
+ mouseUp(element, event);\r
+ }\r
+\r
+ // calculate all interesting variables\r
+ reset(event);\r
+\r
+ if (notHandleMouseDown(element, event)) {\r
+ return true;\r
+ }\r
+\r
+ if (delayConditionMet() && distanceConditionMet(event)) {\r
+ mouseStarted = mouseStart(element, event);\r
+ if (!mouseStarted) {\r
+ event.getOriginalEvent().preventDefault();\r
+ return true;\r
+ }\r
+ }\r
+\r
+ bindOtherMouseEvent(element);\r
+\r
+ event.getOriginalEvent().preventDefault();\r
+\r
+ markEventAsHandled(event);\r
+\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * Method called when the mouse is dragging\r
+ * \r
+ * @param element\r
+ * @param event\r
+ * @return\r
+ */\r
+ protected abstract boolean mouseDrag(Element element, Event event);\r
+\r
+ /**\r
+ * Method called on MouseMove event.\r
+ * \r
+ * You should not override this method. Instead, override\r
+ * {@link #mouseMove(Element, Event)} method\r
+ * \r
+ * @param element\r
+ * @param event\r
+ * @return\r
+ */\r
+ protected boolean mouseMove(Element element, Event event) {\r
+ if (mouseStarted) {\r
+ event.getOriginalEvent().preventDefault();\r
+ return mouseDrag(element, event);\r
+ }\r
+\r
+ if (delayConditionMet() && distanceConditionMet(event)) {\r
+ mouseStarted = mouseStart(element, mouseDownEvent);\r
+ if (mouseStarted) {\r
+ mouseDrag(element, event);\r
+ } else {\r
+ mouseUp(element, event);\r
+ }\r
+ }\r
+\r
+ return !mouseStarted;\r
+ }\r
+\r
+ /**\r
+ * Method called when the mouse is clicked and all conditions for starting the\r
+ * plugin are met.\r
+ * \r
+ * @param element\r
+ * @param event\r
+ * @return\r
+ */\r
+ protected abstract boolean mouseStart(Element element, Event event);\r
+\r
+ /**\r
+ * Method called when the mouse button is released\r
+ * \r
+ * @param element\r
+ * @param event\r
+ * @return\r
+ */\r
+ protected abstract boolean mouseStop(Element element, Event event);\r
+\r
+ /**\r
+ * Method called when mouse is released..\r
+ * \r
+ * You should not override this method. Instead, override\r
+ * {@link #mouseStop(Element, Event)} method\r
+ * \r
+ * @param element\r
+ * @param event\r
+ * @return\r
+ */\r
+ protected boolean mouseUp(Element element, Event event) {\r
+ unbindOtherMouseEvent();\r
+ if (mouseStarted) {\r
+ mouseStarted = false;\r
+ preventClickEvent = (event.getCurrentEventTarget() == mouseDownEvent\r
+ .getCurrentEventTarget());\r
+ mouseStop(element, event);\r
+ }\r
+ return false;\r
+\r
+ }\r
+\r
+ private void bindOtherMouseEvent(final Element element) {\r
+\r
+ $(document).as(Events.Events).bind(Event.ONMOUSEMOVE, getPluginName(),\r
+ (Object) null, new Function() {\r
+ @Override\r
+ public boolean f(com.google.gwt.user.client.Event e) {\r
+ mouseMove(element, Event.create(e));\r
+ return false;\r
+ }\r
+ }).bind(Event.ONMOUSEUP, getPluginName(), (Object) null,\r
+ new Function() {\r
+ @Override\r
+ public boolean f(com.google.gwt.user.client.Event e) {\r
+ mouseUp(element, Event.create(e));\r
+ return false;\r
+ }\r
+ });\r
+ }\r
+\r
+ private boolean delayConditionMet() {\r
+\r
+ if (mouseUpDuration == null) {\r
+ return false;\r
+ }\r
+\r
+ return options.getDelay() <= mouseUpDuration.elapsedMillis();\r
+ }\r
+\r
+ private boolean distanceConditionMet(Event event) {\r
+ int neededDistance = options.getDistance();\r
+ int mouseDownX = mouseDownEvent.getClientX();\r
+ int mouseDownY = mouseDownEvent.getClientY();\r
+ int xMouseDistance = Math.abs(mouseDownX - event.getClientX());\r
+ int yMouseDistance = Math.abs(mouseDownY - event.getClientY());\r
+ // in jQuery-ui we take the greater distance between x and y... not really\r
+ // good !\r
+ // int mouseDistance = Math.max(xMouseDistance, yMouseDistance);\r
+ // use Pythagor theorem !!\r
+ int mouseDistance = (int) Math.sqrt(xMouseDistance * xMouseDistance\r
+ + yMouseDistance * yMouseDistance);\r
+ return mouseDistance >= neededDistance;\r
+ }\r
+\r
+ private native boolean isEventAlreadyHandled(Event event)/*-{\r
+ var result = event.mouseHandled ? event.mouseHandled : false;\r
+ return result;\r
+ }-*/;\r
+\r
+ private native void markEventAsHandled(Event event)/*-{\r
+ event.mouseHandled = true;\r
+ }-*/;\r
+\r
+ private boolean notHandleMouseDown(Element element, Event mouseDownEvent) {\r
+ boolean isNotBoutonLeft = mouseDownEvent.getButton() != NativeEvent.BUTTON_LEFT;\r
+ Element eventTarget = mouseDownEvent.getEventTarget().cast();\r
+\r
+ boolean isElementCancel = false;\r
+ if (options.getCancel() != null) {\r
+ isElementCancel = $(eventTarget).parents().add($(eventTarget)).filter(\r
+ options.getCancel()).length() > 0;\r
+ }\r
+\r
+ return isNotBoutonLeft || isElementCancel\r
+ || !mouseCapture(element, mouseDownEvent);\r
+\r
+ }\r
+\r
+ private void reset(Event mouseDownEvent) {\r
+ this.mouseDownEvent = mouseDownEvent;\r
+ this.mouseUpDuration = new Duration();\r
+ }\r
+\r
+ private void unbindOtherMouseEvent() {\r
+ $(document).as(Events.Events).unbind((Event.ONMOUSEUP | Event.ONMOUSEMOVE),\r
+ getPluginName());\r
+ }\r
+\r
+}\r