]> source.dussan.org Git - gwtquery.git/commitdiff
common-ui plugin classes moved to the trunk
authorJulien Dramaix <julien.dramaix@gmail.com>
Tue, 11 Jan 2011 14:08:14 +0000 (14:08 +0000)
committerJulien Dramaix <julien.dramaix@gmail.com>
Tue, 11 Jan 2011 14:08:14 +0000 (14:08 +0000)
gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/GQueryUi.java [new file with mode: 0755]
gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/MouseOptions.java [new file with mode: 0755]
gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/MousePlugin.java [new file with mode: 0755]

diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/GQueryUi.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/GQueryUi.java
new file mode 100755 (executable)
index 0000000..db8d72e
--- /dev/null
@@ -0,0 +1,246 @@
+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);
+    }
+  }
+
+}
diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/MouseOptions.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/MouseOptions.java
new file mode 100755 (executable)
index 0000000..9487b7e
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * 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"};
+  }
+}
+
diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/MousePlugin.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/MousePlugin.java
new file mode 100755 (executable)
index 0000000..a5cd2b1
--- /dev/null
@@ -0,0 +1,329 @@
+/*\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