From 22d00767500758178ac8d285998e0003b2094352 Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Sat, 17 Jul 2010 17:21:11 +0000 Subject: [PATCH] - Added submit method to gquery, and handle submit events in the events plugins (fixes issue40) - Use the owner document to create new dom elements in order to write in iframes. - Many changes in gquery in order to handle correctly iframe manipulation. - Updated lazy interfaz for GQuery. - Added tests for iframe manipulation and submit events. --- .../com/google/gwt/query/client/GQuery.java | 52 ++++++++--- .../google/gwt/query/client/LazyGQuery.java | 17 +++- .../gwt/query/client/plugins/Events.java | 5 +- .../query/client/plugins/EventsListener.java | 90 ++++++++++++------- .../gwt/query/client/GQueryCoreTest.java | 26 ++++-- .../gwt/query/client/GQueryEventsTest.java | 66 +++++++++++--- 6 files changed, 184 insertions(+), 72 deletions(-) diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/GQuery.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/GQuery.java index cab0ac3e..ed146b12 100644 --- a/gwtquery-core/src/main/java/com/google/gwt/query/client/GQuery.java +++ b/gwtquery-core/src/main/java/com/google/gwt/query/client/GQuery.java @@ -18,6 +18,8 @@ package com.google.gwt.query.client; import static com.google.gwt.query.client.plugins.Effects.Effects; import static com.google.gwt.query.client.plugins.Events.Events; +import java.util.HashMap; + import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.core.client.JsArray; @@ -40,6 +42,7 @@ import com.google.gwt.query.client.css.Percentage; import com.google.gwt.query.client.css.TakesLength; import com.google.gwt.query.client.css.TakesPercentage; import com.google.gwt.query.client.impl.DocumentStyleImpl; +import com.google.gwt.query.client.plugins.EventsListener; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.Window; @@ -161,7 +164,7 @@ public class GQuery implements Lazy { FUNC_BEFORE = 3; private static final String OLD_DATA_PREFIX = "old-"; - + private static JsMap, Plugin> plugins;; @@ -241,8 +244,8 @@ public class GQuery implements Lazy { return $(); } if (selectorOrHtml.trim().charAt(0) == '<') { - Document doc = ctx instanceof Document ? (Document)ctx : document; - return $(clean(selectorOrHtml, doc)); + Document doc = ctx instanceof Document ? ctx.cast() : ctx.getOwnerDocument(); + return $(cleanHtmlString(selectorOrHtml, doc)); } return new GQuery(select(selectorOrHtml, ctx)); } @@ -308,7 +311,7 @@ public class GQuery implements Lazy { } @SuppressWarnings("unchecked") - protected static GQuery clean(String elem, Document doc) { + protected static GQuery cleanHtmlString(String elem, Document doc) { String tags = elem.trim().toLowerCase(); String preWrap = "", postWrap = ""; int wrapPos = 0; @@ -395,7 +398,7 @@ public class GQuery implements Lazy { } private static GQuery innerHtml(String html) { - return $(clean(html, document)); + return $(cleanHtmlString(html, document)); } private static native String[] jsArrayToString0(JsArrayString array) /*-{ @@ -424,7 +427,7 @@ public class GQuery implements Lazy { private String currentSelector; private GQuery previousObject; - + public GQuery() { elements = JavaScriptObject.createArray().cast(); } @@ -446,8 +449,8 @@ public class GQuery implements Lazy { } /** - * Adds the specified classes to each matched element. Add elements to the set - * of matched elements if they are not included yet. + * Add elements to the set of matched elements if they are not included yet. + * It also update the selector appending the new one. */ public GQuery add(GQuery previousObject) { return pushStack(unique(merge(elements, previousObject.elements)), "add", @@ -1177,6 +1180,9 @@ public class GQuery implements Lazy { */ public GQuery html(String html) { for (Element e : elements()) { + if (e.getNodeType() == Node.DOCUMENT_NODE) { + e = e.cast().getBody(); + } e.setInnerHTML(html); } return this; @@ -1968,7 +1974,7 @@ public class GQuery implements Lazy { } public GQuery submit() { - return as(Events).triggerHtmlEvent("submit"); + return as(Events).trigger(EventsListener.ONSUBMIT); } /** @@ -2432,13 +2438,29 @@ public class GQuery implements Lazy { return bind(eventbits, data, funcs); } } + + private GQuery domManip(String htmlString, int func) { + GQuery ret = $(); + HashMap cache = new HashMap(); + for (Element e: elements()) { + Document d = e.getNodeType() == Node.DOCUMENT_NODE ? e.cast() : e.getOwnerDocument(); + GQuery g = cache.get(d); + if (g == null) { + g = cleanHtmlString(htmlString, d); + cache.put(d, g); + } + ret.add(domManip(g, func)); + } + return ret; + } private GQuery domManip(GQuery g, int func) { JSArray newNodes = JSArray.create(); for (int i = 0; i < elements().length; i++) { Element e = elements()[i]; - if (document.equals(e)) { - e = body; + e.getOwnerDocument(); + if (e.getNodeType() == Node.DOCUMENT_NODE) { + e = e.cast().getBody(); } for (int j = 0; j < g.size(); j++) { Node n = g.get(j); @@ -2468,13 +2490,15 @@ public class GQuery implements Lazy { } private GQuery domManip(String html, Document doc, int func) { - return domManip(clean(html, doc), func); + return domManip(html, func); } private native Document getContentDocument(Node n) /*-{ - return n.contentDocument || n.contentWindow.document; + var d = n.contentDocument || n.contentWindow.document; + if (!d.body) d.write(""); + return d; }-*/; - + private native Element getPreviousSiblingElement(Element elem) /*-{ var sib = elem.previousSibling; while (sib && sib.nodeType != 1) diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/LazyGQuery.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/LazyGQuery.java index 8800c8f3..8eaf3289 100644 --- a/gwtquery-core/src/main/java/com/google/gwt/query/client/LazyGQuery.java +++ b/gwtquery-core/src/main/java/com/google/gwt/query/client/LazyGQuery.java @@ -16,6 +16,7 @@ package com.google.gwt.query.client; import static com.google.gwt.query.client.plugins.Effects.Effects; import static com.google.gwt.query.client.plugins.Events.Events; +import java.util.HashMap; import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.core.client.JsArray; @@ -38,6 +39,7 @@ import com.google.gwt.query.client.css.Percentage; import com.google.gwt.query.client.css.TakesLength; import com.google.gwt.query.client.css.TakesPercentage; import com.google.gwt.query.client.impl.DocumentStyleImpl; +import com.google.gwt.query.client.plugins.EventsListener; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.Window; import com.google.gwt.query.client.LazyBase; @@ -45,8 +47,7 @@ import com.google.gwt.query.client.LazyBase; public interface LazyGQuery extends LazyBase{ /** - * Adds the specified classes to each matched element. Add elements to the set - * of matched elements if they are not included yet. + * Add elements to the set of matched elements if they are not included yet. */ LazyGQuery add(GQuery previousObject); @@ -326,7 +327,7 @@ public interface LazyGQuery extends LazyBase{ /** * Stores the value in the named spot with desired return type. */ - Object data(String name, Object value); + LazyGQuery data(String name, Object value); /** * Bind a set of functions to the dblclick event of each matched element. @@ -591,6 +592,11 @@ public interface LazyGQuery extends LazyBase{ */ LazyGQuery keyup(int key); + /** + * Returns the computed left position of the first element matched. + */ + int left(); + /** * Returns the number of elements currently matched. The size function will * return the same value. @@ -1000,6 +1006,11 @@ public interface LazyGQuery extends LazyBase{ */ LazyGQuery toggleClass(String clz, boolean addOrRemove); + /** + * Returns the computed left position of the first element matched. + */ + int top(); + /** * Produces a string representation of the matched elements. */ diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/Events.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/Events.java index 67b8a083..ec1c1d41 100644 --- a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/Events.java +++ b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/Events.java @@ -133,8 +133,11 @@ public class Events extends GQuery { dispatchEvent(document.createErrorEvent()); if ((eventbits | Event.ONMOUSEWHEEL) == Event.ONMOUSEWHEEL) dispatchEvent(document.createMouseEvent("mousewheel", true, true, 0, 0, 0, 0, 0, false, false, false, false, NativeEvent.BUTTON_LEFT, null)); + if (eventbits == EventsListener.ONSUBMIT) + triggerHtmlEvent("submit"); return this; } + /** * Trigger a html event in all matched elements. @@ -143,7 +146,7 @@ public class Events extends GQuery { * An string representing the html event desired */ public Events triggerHtmlEvent(String htmlEvent) { - dispatchEvent(document.createHtmlEvent(htmlEvent, false, false)); + dispatchEvent(document.createHtmlEvent(htmlEvent, true, true)); return this; } diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/EventsListener.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/EventsListener.java index 94beabb1..9155b41a 100644 --- a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/EventsListener.java +++ b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/EventsListener.java @@ -33,16 +33,13 @@ import com.google.gwt.user.client.EventListener; * The class takes care of calling the appropriate functions for each browser * event and it also calls sinkEvents method. */ -class EventsListener implements EventListener { +public class EventsListener implements EventListener { private static class BindFunction { Object data; - Function function; - int times = -1; - int type; BindFunction(int t, Function f, Object d) { @@ -69,6 +66,9 @@ class EventsListener implements EventListener { } } + // Gwt Events class has not this event defined + public static int ONSUBMIT = 0x08000; + public static EventsListener getInstance(Element e) { EventsListener ret = getGQueryEventLinstener(e); return ret != null ? ret : new EventsListener(e); @@ -81,22 +81,38 @@ class EventsListener implements EventListener { private static native EventListener getOriginalEventListener(Element elem) /*-{ return elem.__listener; }-*/; - - private static native void setFocusable(Element elem) /*-{ - elem.tabIndex = 0; - }-*/; - + private static native void setGQueryEventListener(Element elem, EventsListener gqevent) /*-{ elem.__gqueryevent = gqevent; }-*/; + // Gwt does't handle submit events in DOM.sinkEvents + private static native void sinkSubmitEvent(Element elem) /*-{ + if (elem.__gquerysubmit) return; + elem.__gquerysubmit = true; + + var handle = function(event) { + elem.__gqueryevent.@com.google.gwt.query.client.plugins.EventsListener::dispatchEvent(Lcom/google/gwt/user/client/Event;)(event); + }; + + if (elem.addEventListener) + elem.addEventListener("submit", handle, true); + else + elem.attachEvent("onsubmit", handle); + }-*/; + + double lastEvnt=0; + int lastType=0; + + private Element element; - private JsObjectArray elementEvents = JsObjectArray + private JsObjectArray elementEvents = JsObjectArray .createArray().cast(); - private EventListener originalEventListener; + private EventListener originalEventListener; + private EventsListener(Element element) { this.element = element; originalEventListener = getOriginalEventListener(element); @@ -114,21 +130,35 @@ class EventsListener implements EventListener { int times) { if (function == null) { unbind(eventbits); + return; + } + + if (eventbits == ONSUBMIT) { + sinkSubmitEvent(element); } else { + if ((eventbits | Event.FOCUSEVENTS) == Event.FOCUSEVENTS) { + element.setAttribute("tabIndex", "0"); + } DOM.sinkEvents((com.google.gwt.user.client.Element) element, eventbits | DOM.getEventsSunk((com.google.gwt.user.client.Element) element)); - - if ((eventbits | Event.FOCUSEVENTS) == Event.FOCUSEVENTS) { - setFocusable(element); + + } + elementEvents.add(new BindFunction(eventbits, function, data, times)); + } + + public void dispatchEvent(Event event) { + int etype = "submit".equalsIgnoreCase(event.getType()) ? ONSUBMIT + : DOM.eventGetType(event); + for (int i = 0; i < elementEvents.length(); i++) { + BindFunction listener = elementEvents.get(i); + if (listener.hasEventType(etype)) { + if (!listener.fire(event)) { + event.stopPropagation(); + event.preventDefault(); + } } - - elementEvents.add(new EventsListener.BindFunction(eventbits, function, - data, times)); } } - - double lastEvnt=0; - int lastType=0; public void onBrowserEvent(Event event) { // Workaround for Issue_20 @@ -140,27 +170,19 @@ class EventsListener implements EventListener { lastEvnt = Duration.currentTimeMillis(); lastType = event.getTypeInt(); + // Execute the original Gwt listener if (originalEventListener != null) { originalEventListener.onBrowserEvent(event); } - - int etype = DOM.eventGetType(event); - for (int i = 0; i < elementEvents.length(); i++) { - EventsListener.BindFunction listener = elementEvents.get(i); - if (listener.hasEventType(etype)) { - if (!listener.fire(event)) { - event.stopPropagation(); - event.preventDefault(); - } - } - } + + dispatchEvent(event); } - + public void unbind(int eventbits) { - JsObjectArray newList = JsObjectArray + JsObjectArray newList = JsObjectArray .createArray().cast(); for (int i = 0; i < elementEvents.length(); i++) { - EventsListener.BindFunction listener = elementEvents.get(i); + BindFunction listener = elementEvents.get(i); if (!listener.hasEventType(eventbits)) { newList.add(listener); } diff --git a/gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryCoreTest.java b/gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryCoreTest.java index 78dc7e0e..8ebc67de 100644 --- a/gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryCoreTest.java +++ b/gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryCoreTest.java @@ -19,6 +19,7 @@ import static com.google.gwt.query.client.GQuery.$; import static com.google.gwt.query.client.GQuery.$$; import static com.google.gwt.query.client.GQuery.document; +import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.Element; import com.google.gwt.junit.client.GWTTestCase; import com.google.gwt.query.client.impl.SelectorEngineImpl; @@ -190,6 +191,19 @@ public class GQueryCoreTest extends GWTTestCase { assertHtmlEquals("

0

1

2

", $("p", e)); } + public void testIFrameManipulation() { + $(e).html("