diff options
author | Julien Dramaix <julien.dramaix@gmail.com> | 2013-11-22 05:06:40 -0800 |
---|---|---|
committer | Julien Dramaix <julien.dramaix@gmail.com> | 2013-11-22 05:06:40 -0800 |
commit | de46d0577920517ea8f3cb76bca585a62ff7d588 (patch) | |
tree | a6aae8d24fe68291e20f88706383397cde4f1caf | |
parent | 74edc0e4d202ec31bc591f19b63f54523f4ea776 (diff) | |
parent | 2da74cf135e92aec35a58cf9dc45d61fee172938 (diff) | |
download | gwtquery-de46d0577920517ea8f3cb76bca585a62ff7d588.tar.gz gwtquery-de46d0577920517ea8f3cb76bca585a62ff7d588.zip |
Merge pull request #235 from gwtquery/jd_event
Refactor event to accept bitless events and custom events.
7 files changed, 534 insertions, 174 deletions
diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/Function.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/Function.java index 00b41def..e90ad93e 100644 --- a/gwtquery-core/src/main/java/com/google/gwt/query/client/Function.java +++ b/gwtquery-core/src/main/java/com/google/gwt/query/client/Function.java @@ -422,7 +422,7 @@ public abstract class Function { * Override this method for bound event handlers if you wish to deal with * per-handler user data. */ - public boolean f(Event e, Object arg) { + public boolean f(Event e, Object... arg) { setArguments(arg); setEvent(e); return f(e); @@ -527,16 +527,16 @@ public abstract class Function { * catch the exception and send it to the GWT UncaughtExceptionHandler * They are intentionally final to avoid override them */ - public final boolean fe(Event ev, Object arg) { + public final boolean fe(Event ev, Object... args) { if (GWT.getUncaughtExceptionHandler() != null) { try { - return f(ev, arg); + return f(ev, args); } catch (Exception e) { GWT.getUncaughtExceptionHandler().onUncaughtException(e); } return true; } - return f(ev, arg); + return f(ev, args); } /** 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 270ad1cc..5950f6bf 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 @@ -1309,6 +1309,17 @@ public class GQuery implements Lazy<GQuery, LazyGQuery> { } /** + * Bind Handlers or fire Events for each matched element. + */ + private GQuery bindOrFire(String eventname, final Object data, final Function... funcs) { + if (funcs.length == 0) { + return as(Events).triggerHtmlEvent(eventname); + } else { + return bind(eventname, data, funcs); + } + } + + /** * Bind a set of functions to the blur event of each matched element. Or trigger the blur event if * no functions are provided. */ @@ -1453,7 +1464,6 @@ public class GQuery implements Lazy<GQuery, LazyGQuery> { * DOM. This method allows retrieving the list of ancestors matching many selectors by traversing * the DOM only one time. * - * @param selector * @return */ public JsNamedArray<NodeList<Element>> closest(String[] selectors) { @@ -1466,7 +1476,6 @@ public class GQuery implements Lazy<GQuery, LazyGQuery> { * DOM until reach the <code>context</code> node.. This method allows retrieving the list of * ancestors matching many selectors by traversing the DOM only one time. * - * @param selector * @return */ public JsNamedArray<NodeList<Element>> closest(String[] selectors, Node context) { @@ -3032,7 +3041,6 @@ public class GQuery implements Lazy<GQuery, LazyGQuery> { * Get all following siblings of each element up to but not including the element matched by the * DOM node, filtered by a selector. * - * @param selector * @return */ public GQuery nextUntil(Element until, String filter) { @@ -3043,7 +3051,6 @@ public class GQuery implements Lazy<GQuery, LazyGQuery> { * Get all following siblings of each element up to but not including the element matched by the * GQuery object. * - * @param selector * @return */ public GQuery nextUntil(GQuery until) { @@ -3054,7 +3061,6 @@ public class GQuery implements Lazy<GQuery, LazyGQuery> { * Get all following siblings of each element up to but not including the element matched by the * GQuery object, filtered by a selector * - * @param selector * @return */ public GQuery nextUntil(GQuery until, String filter) { @@ -3904,14 +3910,14 @@ public class GQuery implements Lazy<GQuery, LazyGQuery> { * */ public GQuery resize(Function... f) { - return bindOrFire(EventsListener.ONRESIZE, null, f); + return bindOrFire("resize", null, f); } /** * Bind an event handler to the "resize" JavaScript event, or trigger that event on an element. */ public GQuery resize(final Function f) { - return bindOrFire(EventsListener.ONRESIZE, null, f); + return bindOrFire("resize", null, f); } /** @@ -4257,7 +4263,7 @@ public class GQuery implements Lazy<GQuery, LazyGQuery> { * functions are provided. */ public GQuery submit(Function... funcs) { - return bindOrFire(EventsListener.ONSUBMIT, null, funcs); + return bindOrFire("submit", null, funcs); } /** @@ -4394,6 +4400,17 @@ public class GQuery implements Lazy<GQuery, LazyGQuery> { return as(Events).trigger(eventbits, keys); } + + /** + * Trigger a event in all matched elements. + * + * @param eventName An string representing the type of the event desired + * @param datas Additional parameters to pass along to the event handlers. + */ + public GQuery trigger(String eventName, Object... datas) { + return as(Events).triggerHtmlEvent(eventName, datas); + } + /** * Removes all events that match the eventbits. */ 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 472aed62..da0de164 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 @@ -485,7 +485,6 @@ public interface LazyGQuery<T> extends LazyBase<T>{ * DOM. This method allows retrieving the list of ancestors matching many selectors by traversing * the DOM only one time. * - * @param selector * @return */ JsNamedArray<NodeList<Element>> closest(String[] selectors); @@ -496,7 +495,6 @@ public interface LazyGQuery<T> extends LazyBase<T>{ * DOM until reach the <code>context</code> node.. This method allows retrieving the list of * ancestors matching many selectors by traversing the DOM only one time. * - * @param selector * @return */ JsNamedArray<NodeList<Element>> closest(String[] selectors, Node context); @@ -1522,7 +1520,6 @@ public interface LazyGQuery<T> extends LazyBase<T>{ * Get all following siblings of each element up to but not including the element matched by the * DOM node, filtered by a selector. * - * @param selector * @return */ LazyGQuery<T> nextUntil(Element until, String filter); @@ -1531,7 +1528,6 @@ public interface LazyGQuery<T> extends LazyBase<T>{ * Get all following siblings of each element up to but not including the element matched by the * GQuery object. * - * @param selector * @return */ LazyGQuery<T> nextUntil(GQuery until); @@ -1540,7 +1536,6 @@ public interface LazyGQuery<T> extends LazyBase<T>{ * Get all following siblings of each element up to but not including the element matched by the * GQuery object, filtered by a selector * - * @param selector * @return */ LazyGQuery<T> nextUntil(GQuery until, String filter); 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 f9ee5519..94d1ab8d 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 @@ -14,6 +14,7 @@ package com.google.gwt.query.client.plugins; import com.google.gwt.dom.client.Element; +import com.google.gwt.dom.client.FormElement; import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.dom.client.Node; import com.google.gwt.query.client.Function; @@ -109,7 +110,7 @@ public class Events extends GQuery { public GQuery die(int eventbits, String nameSpace) { EventsListener.getInstance(Element.is(currentContext) ? (Element) currentContext : body).die( - eventbits, nameSpace, null, currentSelector); + eventbits, nameSpace, null, null, currentSelector); return this; } @@ -136,7 +137,7 @@ public class Events extends GQuery { public GQuery live(int eventbits, String nameSpace, final Object data, Function... funcs) { EventsListener.getInstance(Element.is(currentContext) ? (Element) currentContext : body).live( - eventbits, nameSpace, null, currentSelector, data, funcs); + eventbits, nameSpace, null, null, currentSelector, data, funcs); return this; } @@ -166,8 +167,6 @@ public class Events extends GQuery { return bind("mouseenter", null, f); } - // TODO handle unbind !! - /** * Bind an event handler to be fired when the mouse leaves an element, or trigger that handler on * an element if no functions are provided. @@ -258,26 +257,26 @@ public class Events extends GQuery { 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) { - Event evt = document.createHtmlEvent("submit", true, true).cast(); - dispatchEvent(evt, new Function() { - public native void f(Element e) /*-{ - e.submit(); - }-*/; - }); - } - if (eventbits == EventsListener.ONRESIZE) - triggerHtmlEvent("resize"); return this; } /** * Trigger a html event in all matched elements. * - * @param htmlEvent An string representing the html event desired + * @param htmlEvent A string representing the desired html event. * @functions a set of function to run if the event is not canceled. */ public Events triggerHtmlEvent(String htmlEvent, Function... functions) { + return triggerHtmlEvent(htmlEvent, null, functions); + } + + /** + * Trigger a html event in all matched elements. + * + * @param htmlEvent An string representing the desired html event. + * @functions a set of function to run if the event is not canceled. + */ + public Events triggerHtmlEvent(String htmlEvent, Object[] datas, final Function... functions) { SpecialEvent specialEvent = EventsListener.special.get(htmlEvent); boolean isSpecialEvent = specialEvent != null; @@ -289,7 +288,21 @@ public class Events extends GQuery { if (isSpecialEvent) { GqEvent.setOriginalEventType(e, originalEventName); } - dispatchEvent(e, functions); + + if ("submit".equals(htmlEvent)){ + Function submitFunction = new Function() { + public void f(Element e) { + // first submit the form then call the others functions + if (FormElement.is(e)) { + e.<FormElement>cast().submit(); + } + callHandlers(e, getEvent(), functions); + } + }; + dispatchEvent(e, datas, submitFunction); + } else { + dispatchEvent(e, datas, functions); + } return this; } @@ -315,7 +328,7 @@ public class Events extends GQuery { public Events unbind(int eventbits, String name, Function f) { for (Element e : elements()) { if (isEventCapable(e)) { - EventsListener.getInstance(e).unbind(eventbits, name, null, f); + EventsListener.getInstance(e).unbind(eventbits, name, null, null, f); } } return this; @@ -357,17 +370,27 @@ public class Events extends GQuery { } private void dispatchEvent(NativeEvent evt, Function... funcs) { + dispatchEvent(evt, null, funcs); + } + + private void dispatchEvent(NativeEvent evt, Object[] datas, Function... funcs) { for (Element e : elements()) { if (isEventCapable(e)) { + $(e).data(EventsListener.EVENT_DATA, datas); e.dispatchEvent(evt); if (!JsUtils.isDefaultPrevented(evt)) { - for (Function f : funcs) { - f.setEvent(Event.as(evt)); - f.f(e); - } + callHandlers(e, evt, funcs); } + $(e).removeData(EventsListener.EVENT_DATA); } } } + private void callHandlers(Element e, NativeEvent evt, Function... functions){ + for (Function f : functions) { + f.setEvent(Event.as(evt)); + f.f(e); + } + } + } diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/events/EventsListener.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/events/EventsListener.java index f348adaf..eacc9d18 100644 --- a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/events/EventsListener.java +++ b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/events/EventsListener.java @@ -31,6 +31,7 @@ import com.google.gwt.user.client.Event; import com.google.gwt.user.client.EventListener; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** @@ -91,7 +92,7 @@ public class EventsListener implements EventListener { } @Override - public boolean f(Event e, Object data) { + public boolean f(Event e, Object... data) { EventTarget eventTarget = e.getCurrentEventTarget(); Element target = eventTarget != null ? eventTarget.<Element> cast() : null; @@ -115,37 +116,50 @@ public class EventsListener implements EventListener { Object data; Function function; - String nameSpace = ""; + String nameSpace; // for special event like mouseleave, mouseenter String originalEventType; - int times = -1; + int times; int type; + String eventName; - BindFunction(int t, String n, String originalEventType, Function f, Object d) { - type = t; - function = f; - data = d; - this.originalEventType = originalEventType; - if (n != null) { - nameSpace = n; - } - } - - BindFunction(int t, String n, String originalEventType, Function f, Object d, int times) { - this(t, n, originalEventType, f, d); + BindFunction(int type, String eventName, String nameSpace, String originalEventType, + Function function, Object data, int times) { this.times = times; + this.eventName = eventName; + this.type = type; + this.function = function; + this.data = data; + this.originalEventType = originalEventType; + this.nameSpace = nameSpace != null ? nameSpace : ""; } - public boolean fire(Event event) { + public boolean fire(Event event, Object[] eventData) { if (times != 0) { times--; - return function.fe(event, data); + Object[] arguments; + eventData = eventData != null ? eventData : new Object[0]; + // The argument of the function will be first the data attached to the handler then the + // data attached to the event. + if (data != null) { + Object[] handlerData = data.getClass().isArray() ? (Object[]) data : new Object[]{data}; + arguments = new Object[handlerData.length + eventData.length]; + System.arraycopy(handlerData, 0, arguments, 0, handlerData.length); + System.arraycopy(eventData, 0, arguments, handlerData.length, eventData.length); + } else { + arguments = eventData; + } + return function.fe(event, arguments); } return true; } public boolean hasEventType(int etype) { - return (type & etype) != 0; + return type != BITLESS && (type & etype) != 0; + } + + public boolean isTypeOf(String eName) { + return eventName != null && eventName.equalsIgnoreCase(eName); } /** @@ -190,8 +204,13 @@ public class EventsListener implements EventListener { JsNamedArray<JsObjectArray<BindFunction>> bindFunctionBySelector; - LiveBindFunction(int type, String namespace) { - super(type, namespace, null, null, -1); + LiveBindFunction(String eventName, String namespace, Object data) { + super(BITLESS, eventName, namespace, null, null, data, -1); + clean(); + } + + LiveBindFunction(int type, String namespace, Object data) { + super(type, null, namespace, null, null, data, -1); clean(); } @@ -213,7 +232,7 @@ public class EventsListener implements EventListener { } @Override - public boolean fire(Event event) { + public boolean fire(Event event, Object[] eventData) { if (isEmpty()) { return true; } @@ -233,7 +252,7 @@ public class EventsListener implements EventListener { JsObjectArray<BindFunction> bindFunctions = bindFunctionBySelector.get(cssSelector); for (int i = 0; bindFunctions != null && i < bindFunctions.length(); i++) { BindFunction f = bindFunctions.get(i); - if (f.hasEventType(event.getTypeInt())) { + if (f.hasEventType(event.getTypeInt()) || f.isTypeOf(event.getType())) { validSelectors.add(cssSelector); break; } @@ -254,7 +273,7 @@ public class EventsListener implements EventListener { JsObjectArray<BindFunction> bindFunctions = bindFunctionBySelector.get(cssSelector); for (int i = 0; bindFunctions != null && i < bindFunctions.length(); i++) { BindFunction f = bindFunctions.get(i); - if (f.hasEventType(event.getTypeInt())) { + if (f.hasEventType(event.getTypeInt()) || f.isTypeOf(event.getType())) { NodeList<Element> n = realCurrentTargetBySelector.get(cssSelector); for (int j = 0; n != null && j < n.getLength(); j++) { Element element = n.getItem(j); @@ -262,8 +281,9 @@ public class EventsListener implements EventListener { // handlers for this element bound to this element if (stopElement == null || element.equals(stopElement)) { gqEvent.setCurrentElementTarget(element); - - if (!f.fire(gqEvent)) { + // data + eventData = $(element).data(EVENT_DATA); + if (!f.fire(gqEvent, eventData)) { stopElement = element; } } @@ -354,8 +374,9 @@ public class EventsListener implements EventListener { } - public static int ONSUBMIT = GqEvent.ONSUBMIT; - public static int ONRESIZE = GqEvent.ONRESIZE; + public static final String EVENT_DATA = "___event_datas"; + public static final int BITLESS = -1; + public static String MOUSEENTER = "mouseenter"; public static String MOUSELEAVE = "mouseleave"; @@ -382,7 +403,10 @@ public class EventsListener implements EventListener { public static void rebind(Element e) { EventsListener ret = getGQueryEventListener(e); if (ret != null && ret.eventBits != 0) { - ret.sink(); + // rebind bit event + ret.sink(ret.eventBits, null); + + // TODO manage bitless event } } @@ -407,7 +431,7 @@ public class EventsListener implements EventListener { }-*/; // Gwt does't handle submit nor resize events in DOM.sinkEvents - private static native void sinkEvent(Element elem, String name) /*-{ + private static native void sinkBitlessEvent(Element elem, String name) /*-{ if (!elem.__gquery) elem.__gquery = []; if (elem.__gquery[name]) @@ -415,7 +439,7 @@ public class EventsListener implements EventListener { elem.__gquery[name] = true; var handle = function(event) { - elem.__gqueryevent.@com.google.gwt.query.client.plugins.events.EventsListener::dispatchEvent(Lcom/google/gwt/user/client/Event;)(event); + elem.__gqueryevent.@com.google.gwt.query.client.plugins.events.EventsListener::onBrowserEvent(Lcom/google/gwt/user/client/Event;)(event); }; if (elem.addEventListener) @@ -426,14 +450,12 @@ public class EventsListener implements EventListener { int eventBits = 0; double lastEvnt = 0; - - int lastType = 0; + String lastType = ""; private Element element; - private JsObjectArray<BindFunction> elementEvents = JsObjectArray.createArray().cast(); - private JsMap<Integer, LiveBindFunction> liveBindFunctionByEventType = JsMap.create(); + private JsMap<String, LiveBindFunction> liveBindFunctionByEventName = JsMap.create(); private EventsListener(Element element) { this.element = element; @@ -454,16 +476,9 @@ public class EventsListener implements EventListener { } } - public void bind(int eventbits, String namespace, String originalEventType, final Object data, - final Function function, int times) { - if (function == null) { - unbind(eventbits, namespace, originalEventType, null); - return; - } - eventBits |= eventbits; - sink(); - elementEvents.add(new BindFunction(eventbits, namespace, originalEventType, function, data, - times)); + public void bind(int eventbits, String namespace, String originalEventType, Object data, + Function function, int times) { + bind(eventbits, namespace, null, originalEventType, data, function, times); } public void bind(String events, final Object data, Function... funcs) { @@ -488,14 +503,27 @@ public class EventsListener implements EventListener { eventName = hook != null ? hook.getDelegateType() : eventName; String originalEventName = hook != null ? hook.getOriginalType() : null; - int b = getTypeInt(eventName); + int b = Event.getTypeInt(eventName); for (Function function : funcs) { Function handler = hook != null ? hook.createDelegateHandler(function) : function; - bind(b, nameSpace, originalEventName, data, handler, -1); + bind(b, nameSpace, eventName, originalEventName, data, handler, -1); } } } + private void bind(int eventbits, String namespace, String eventName, String originalEventType, + Object data, Function function, int times) { + if (function == null) { + unbind(eventbits, namespace, eventName, originalEventType, null); + return; + } + + sink(eventbits, eventName); + + elementEvents.add(new BindFunction(eventbits, eventName, namespace, originalEventType, + function, data, times)); + } + public void die(String eventNames, String cssSelector) { String[] parts = eventNames.split("[\\s,]+"); @@ -512,50 +540,66 @@ public class EventsListener implements EventListener { eventName = subparts[0]; } - //handle special event like mouseenter or mouseleave SpecialEvent hook = special.get(eventName); eventName = hook != null ? hook.getDelegateType() : eventName; String originalEventName = hook != null ? hook.getOriginalType() : null; - int b = getTypeInt(eventName); + int b = Event.getTypeInt(eventName); - die(b, nameSpace, originalEventName, cssSelector); + die(b, nameSpace, eventName, originalEventName, cssSelector); } } - public void die(int eventbits, String nameSpace, String originalEventName,String cssSelector) { + public void die(int eventbits, String nameSpace, String eventName, String originalEventName, + String cssSelector) { if (eventbits <= 0) { + if (eventName != null) { + LiveBindFunction liveBindFunction = liveBindFunctionByEventName.get(eventName); + maybeRemoveLiveBindFunction(liveBindFunction, cssSelector, BITLESS, eventName, nameSpace, + originalEventName); + } + + // if eventbits == -1 and eventName is null, remove all event handlers for this selector for (String k : liveBindFunctionByEventType.keys()) { - LiveBindFunction liveBindFunction = liveBindFunctionByEventType.<JsCache> cast().get(k); - liveBindFunction.removeBindFunctionForSelector(cssSelector, nameSpace, null); - if (liveBindFunction.isEmpty()){ - liveBindFunctionByEventType.<JsCache>cast().delete(k); - } + int bits = Integer.parseInt(k); + LiveBindFunction liveBindFunction = liveBindFunctionByEventType.get(bits); + maybeRemoveLiveBindFunction(liveBindFunction, cssSelector, bits, null, nameSpace, null); } } else { LiveBindFunction liveBindFunction = liveBindFunctionByEventType.get(eventbits); - if (liveBindFunction != null) { - liveBindFunction.removeBindFunctionForSelector(cssSelector, nameSpace, originalEventName); - } + maybeRemoveLiveBindFunction(liveBindFunction, cssSelector, eventbits, null, nameSpace, + originalEventName); + } + } + + private void maybeRemoveLiveBindFunction(LiveBindFunction liveBindFunction, String cssSelector, + int eventbits, String eventName, String nameSpace, String originalEventName) { + if (liveBindFunction != null) { + liveBindFunction.removeBindFunctionForSelector(cssSelector, nameSpace, originalEventName); if (liveBindFunction.isEmpty()){ - liveBindFunctionByEventType.remove(eventbits); + if (eventbits != BITLESS) { + liveBindFunctionByEventType.remove(eventbits); + } else { + liveBindFunctionByEventName.remove(eventName); + } } } } public void dispatchEvent(Event event) { - int etype = getTypeInt(event.getType()); + String ename = event.getType(); + int etype = Event.getTypeInt(ename); String originalEventType = GqEvent.getOriginalEventType(event); + Object[] handlerData = $(element).data(EVENT_DATA); for (int i = 0, l = elementEvents.length(); i < l; i++) { BindFunction listener = elementEvents.get(i); - if (listener != null && listener.hasEventType(etype) - && (originalEventType == null || originalEventType - .equals(listener.getOriginalEventType()))) { - if (!listener.fire(event)) { + if (listener != null && (listener.hasEventType(etype) || listener.isTypeOf(ename)) + && (originalEventType == null || originalEventType.equals(listener.getOriginalEventType()))) { + if (!listener.fire(event, handlerData)) { event.stopPropagation(); event.preventDefault(); } @@ -593,15 +637,42 @@ public class EventsListener implements EventListener { eventName = hook != null ? hook.getDelegateType() : eventName; String originalEventName = hook != null ? hook.getOriginalType() : null; - int b = getTypeInt(eventName); + int b = Event.getTypeInt(eventName); for (Function function : funcs) { Function handler = hook != null ? hook.createDelegateHandler(function) : function; - live(b, nameSpace, originalEventName, cssSelector, data, handler); + live(b, nameSpace, eventName, originalEventName, cssSelector, data, handler); } } } - public void live(int eventbits, String nameSpace, String originalEventName, String cssSelector, Object data, Function... funcs) { + public void live(int eventbits, String nameSpace, String eventName, String originalEventName, + String cssSelector, Object data, Function... funcs) { + if (eventbits != BITLESS) { + liveBitEvent(eventbits, nameSpace, originalEventName, cssSelector, data, funcs); + } else { + liveBitlessEvent(eventName, nameSpace, originalEventName, cssSelector, data, funcs); + } + } + + private void liveBitlessEvent(String eventName, String nameSpace, String originalEventName, + String cssSelector, Object data, Function... funcs) { + LiveBindFunction liveBindFunction = liveBindFunctionByEventName.get(eventName); + + if (liveBindFunction == null) { + liveBindFunction = new LiveBindFunction(eventName, "live", data); + sink(BITLESS, eventName); + elementEvents.add(liveBindFunction); + liveBindFunctionByEventName.put(eventName, liveBindFunction); + } + + for (Function f : funcs) { + liveBindFunction.addBindFunctionForSelector(cssSelector, new BindFunction(BITLESS, eventName, + nameSpace, originalEventName, f, data, -1)); + } + } + + private void liveBitEvent(int eventbits, String nameSpace, String originalEventName, + String cssSelector, Object data, Function... funcs) { for (int i = 0; i < 28; i++) { int event = (int) Math.pow(2, i); if ((eventbits & event) == event) { @@ -609,16 +680,15 @@ public class EventsListener implements EventListener { // is a LiveBindFunction already attached for this kind of event LiveBindFunction liveBindFunction = liveBindFunctionByEventType.get(event); if (liveBindFunction == null) { - liveBindFunction = new LiveBindFunction(event, "live"); - eventBits |= event; - sink(); + liveBindFunction = new LiveBindFunction(event, "live", data); + sink(eventbits, null); elementEvents.add(liveBindFunction); liveBindFunctionByEventType.put(event, liveBindFunction); } for (Function f : funcs) { - liveBindFunction.addBindFunctionForSelector(cssSelector, new BindFunction(event, nameSpace, - originalEventName, f, data)); + liveBindFunction.addBindFunctionForSelector(cssSelector, new BindFunction(event, + null, nameSpace, originalEventName, f, data, -1)); } } } @@ -627,15 +697,15 @@ public class EventsListener implements EventListener { public void onBrowserEvent(Event event) { double now = Duration.currentTimeMillis(); // Workaround for Issue_20 - if (lastType == event.getTypeInt() && now - lastEvnt < 10 + if (lastType.equals(event.getType()) && now - lastEvnt < 10 && "body".equalsIgnoreCase(element.getTagName())) { return; } lastEvnt = now; - lastType = event.getTypeInt(); + lastType = event.getType(); // Execute the original Gwt listener - if (getOriginalEventListener() != null) { + if (getOriginalEventListener() != null && getOriginalEventListener() != this) { getOriginalEventListener().onBrowserEvent(event); } @@ -643,10 +713,11 @@ public class EventsListener implements EventListener { } public void unbind(int eventbits) { - unbind(eventbits, null, null, null); + unbind(eventbits, null, null, null, null); } - public void unbind(int eventbits, String namespace, String originalEventType, Function f) { + public void unbind(int eventbits, String namespace, String eventName, String originalEventType, + Function f) { JsObjectArray<BindFunction> newList = JsObjectArray.createArray().cast(); for (int i = 0; i < elementEvents.length(); i++) { BindFunction listener = elementEvents.get(i); @@ -654,13 +725,13 @@ public class EventsListener implements EventListener { boolean matchNS = namespace == null || namespace.isEmpty() || listener.nameSpace.equals(namespace); boolean matchEV = eventbits <= 0 || listener.hasEventType(eventbits); - boolean matchOEVT = - (originalEventType == null && listener.getOriginalEventType() == null) + boolean matchEVN = matchEV || listener.isTypeOf(eventName); + boolean matchOEVT = (originalEventType == null && listener.getOriginalEventType() == null) || (originalEventType != null && originalEventType.equals(listener - .getOriginalEventType())); + .getOriginalEventType())); boolean matchFC = f == null || listener.isEquals(f); - if (matchNS && matchEV && matchFC && matchOEVT) { + if (matchNS && matchEV && matchEVN && matchFC && matchOEVT) { int currentEventbits = listener.unsink(eventbits); if (currentEventbits == 0) { @@ -697,9 +768,9 @@ public class EventsListener implements EventListener { eventName = hook != null ? hook.getDelegateType() : eventName; String originalEventName = hook != null ? hook.getOriginalType() : null; - int b = getTypeInt(eventName); + int b = Event.getTypeInt(eventName); - unbind(b, nameSpace, originalEventName, f); + unbind(b, nameSpace, eventName, originalEventName, f); } } @@ -709,15 +780,14 @@ public class EventsListener implements EventListener { liveBindFunctionByEventType = JsMap.create(); } - private void sink() { + private void sink(int eventbits, String eventName) { // ensure that the gwtQuery's event listener is set as event listener of the element DOM.setEventListener((com.google.gwt.user.client.Element) element, this); - if (eventBits == ONSUBMIT) { - sinkEvent(element, "submit"); - } else if ((eventBits | ONRESIZE) == ONRESIZE) { - sinkEvent(element, "resize"); - } else { - if ((eventBits | Event.FOCUSEVENTS) == Event.FOCUSEVENTS + + if (eventbits != BITLESS) { + eventBits |= eventbits; + + if ((eventBits | Event.FOCUSEVENTS) == Event.FOCUSEVENTS && JsUtils.isElement(element) && element.getAttribute("tabIndex").length() == 0) { element.setAttribute("tabIndex", "0"); @@ -725,29 +795,11 @@ public class EventsListener implements EventListener { DOM.sinkEvents((com.google.gwt.user.client.Element) element, eventBits | DOM.getEventsSunk((com.google.gwt.user.client.Element) element)); + } else { + sinkBitlessEvent(element, eventName); } } - private int getEventBits(String... events) { - int ret = 0; - for (String e : events) { - String[] parts = e.split("[\\s,]+"); - for (String s : parts) { - int event = getTypeInt(s); - if (event > 0) { - ret |= event; - } - } - } - - return ret; - } - - private int getTypeInt(String eventName) { - return "submit".equals(eventName) ? ONSUBMIT : "resize".equals(eventName) ? ONRESIZE : Event - .getTypeInt(eventName); - } - public void cleanEventDelegation() { for (String k : liveBindFunctionByEventType.keys()) { LiveBindFunction function = liveBindFunctionByEventType.<JsCache> cast().get(k); diff --git a/gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryCoreTestGwt.java b/gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryCoreTestGwt.java index 416d142e..c86b67e4 100644 --- a/gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryCoreTestGwt.java +++ b/gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryCoreTestGwt.java @@ -1859,8 +1859,8 @@ public class GQueryCoreTestGwt extends GWTTestCase { assertEquals("E", g.text()); g.unbind(Event.ONCLICK).bind(Event.ONCLICK, "D", new Function(){ @Override - public boolean f(Event e, Object o) { - $(e).text("E" + o); + public boolean f(Event e, Object... o) { + $(e).text("E" + o[0]); return false; } }).click(); diff --git a/gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryEventsTestGwt.java b/gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryEventsTestGwt.java index 768a376d..b13b3b4e 100644 --- a/gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryEventsTestGwt.java +++ b/gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryEventsTestGwt.java @@ -13,23 +13,6 @@ */ package com.google.gwt.query.client; -import static com.google.gwt.query.client.GQuery.$; -import static com.google.gwt.query.client.GQuery.document; -import static com.google.gwt.query.client.GQuery.lazy; -import static com.google.gwt.user.client.Event.FOCUSEVENTS; -import static com.google.gwt.user.client.Event.KEYEVENTS; -import static com.google.gwt.user.client.Event.MOUSEEVENTS; -import static com.google.gwt.user.client.Event.ONBLUR; -import static com.google.gwt.user.client.Event.ONFOCUS; -import static com.google.gwt.user.client.Event.ONKEYDOWN; -import static com.google.gwt.user.client.Event.ONKEYPRESS; -import static com.google.gwt.user.client.Event.ONKEYUP; -import static com.google.gwt.user.client.Event.ONMOUSEDOWN; -import static com.google.gwt.user.client.Event.ONMOUSEMOVE; -import static com.google.gwt.user.client.Event.ONMOUSEOUT; -import static com.google.gwt.user.client.Event.ONMOUSEOVER; -import static com.google.gwt.user.client.Event.ONMOUSEUP; - import com.google.gwt.dom.client.Element; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; @@ -43,16 +26,41 @@ import com.google.gwt.query.client.plugins.Events; import com.google.gwt.query.client.plugins.events.EventsListener; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.Timer; -import com.google.gwt.user.client.Window; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.HTML; import com.google.gwt.user.client.ui.RootPanel; +import static com.google.gwt.query.client.GQuery.$; +import static com.google.gwt.query.client.GQuery.document; +import static com.google.gwt.query.client.GQuery.lazy; +import static com.google.gwt.user.client.Event.FOCUSEVENTS; +import static com.google.gwt.user.client.Event.KEYEVENTS; +import static com.google.gwt.user.client.Event.MOUSEEVENTS; +import static com.google.gwt.user.client.Event.ONBLUR; +import static com.google.gwt.user.client.Event.ONFOCUS; +import static com.google.gwt.user.client.Event.ONKEYDOWN; +import static com.google.gwt.user.client.Event.ONKEYPRESS; +import static com.google.gwt.user.client.Event.ONKEYUP; +import static com.google.gwt.user.client.Event.ONMOUSEDOWN; +import static com.google.gwt.user.client.Event.ONMOUSEMOVE; +import static com.google.gwt.user.client.Event.ONMOUSEOUT; +import static com.google.gwt.user.client.Event.ONMOUSEOVER; +import static com.google.gwt.user.client.Event.ONMOUSEUP; + /** * Test class for testing gwt events plugin api. */ public class GQueryEventsTestGwt extends GWTTestCase { + private static class CounterFunction extends Function { + private int invokationCounter; + + @Override + public void f() { + invokationCounter++; + } + } + static Element e = null; static HTML testPanel = null; @@ -146,7 +154,6 @@ public class GQueryEventsTestGwt extends GWTTestCase { assertEquals("", $("#test", e).val()); } - } public void testBindUnbindSubmitEvent() { @@ -156,7 +163,7 @@ public class GQueryEventsTestGwt extends GWTTestCase { testSubmitEventCont = 0; // Add an onsubmit function to the form returning false to cancel the action - $("form").bind(EventsListener.ONSUBMIT, null, new Function() { + $("form").bind("submit", null, new Function() { public boolean f(Event e) { testSubmitEventCont++; return false; @@ -168,7 +175,7 @@ public class GQueryEventsTestGwt extends GWTTestCase { assertEquals(1, testSubmitEventCont); // Remove the binding - $("form").unbind(EventsListener.ONSUBMIT); + $("form").unbind("submit"); // Check that on submit function is not called and the form has been // submitted @@ -1024,6 +1031,7 @@ public class GQueryEventsTestGwt extends GWTTestCase { public void testResizeWindowEvent() { GQuery w = $(GQuery.window); + delayTestFinish(100); w.bind("resize", null, new Function() { public void f(Element e) { @@ -1031,7 +1039,8 @@ public class GQueryEventsTestGwt extends GWTTestCase { } }); - Window.resizeTo(w.width(), w.height() + 100); + w.trigger("resize"); + } /** @@ -1047,7 +1056,7 @@ public class GQueryEventsTestGwt extends GWTTestCase { testSubmitEventCont = 0; // Add an onsubmit function to the form returning false to cancel the action - $("form").bind(EventsListener.ONSUBMIT, null, new Function() { + $("form").bind("submit", null, new Function() { public boolean f(Event e) { testSubmitEventCont++; return false; @@ -1060,7 +1069,7 @@ public class GQueryEventsTestGwt extends GWTTestCase { assertFalse($("#miframe").contents().find("body").text().contains("ERROR")); // Remove the binding - $("form").unbind(EventsListener.ONSUBMIT); + $("form").unbind("submit"); // Check that on submit function is not called and the form has been // submitted @@ -1314,4 +1323,268 @@ public class GQueryEventsTestGwt extends GWTTestCase { div.click(); assertEquals(1, count[0]); } + + public void testCustomEvent() { + $(e).html("<div id='target'>"); + GQuery target = $("#target", e); + + CounterFunction handler = new CounterFunction(); + + // test custom event binding + target.bind("mycustomevent", handler); + + target.trigger("mycustomevent"); + + assertEquals(1, handler.invokationCounter); + + // test custom event unbinding + target.unbind("mycustomevent"); + + handler.invokationCounter = 0; + + target.trigger("mycustomevent"); + + assertEquals(0, handler.invokationCounter); + } + + public void testCustomEventWithEventData() { + $(e).html("<div id='target'>"); + GQuery target = $("#target", e); + + CounterFunction handler = new CounterFunction(); + + // test custom event binding with event data + target.bind("mycustomevent", "handlerdata0", handler); + + target.trigger("mycustomevent"); + + assertEquals(1, handler.invokationCounter); + assertEquals("handlerdata0", handler.getArgument(0)); + + // unbind + target.unbind("mycustomevent"); + handler.invokationCounter = 0; + target.trigger("mycustomevent"); + assertEquals(0, handler.invokationCounter); + + // test custom event binding with event data as array + target.bind("mycustomevent", new String[]{"handlerdata0", "handlerdata1"}, handler); + + target.trigger("mycustomevent"); + + assertEquals(1, handler.invokationCounter); + assertEquals("handlerdata0", handler.getArgument(0)); + assertEquals("handlerdata1", handler.getArgument(1)); + } + + public void testCustomEventWithHandlerData() { + $(e).html("<div id='target'>"); + GQuery target = $("#target", e); + + CounterFunction handler = new CounterFunction(); + + // test custom event binding + target.bind("mycustomevent", handler); + + target.trigger("mycustomevent", "eventdata0", "eventdata1"); + + assertEquals(1, handler.invokationCounter); + assertEquals("eventdata0", handler.getArgument(0)); + assertEquals("eventdata1", handler.getArgument(1)); + + // unbind + target.unbind("mycustomevent"); + handler.invokationCounter = 0; + target.trigger("mycustomevent"); + assertEquals(0, handler.invokationCounter); + } + + + public void testCustomEventWithEventDataAndHandlerData() { + $(e).html("<div id='target'>"); + GQuery target = $("#target", e); + + CounterFunction handler = new CounterFunction(); + + // test custom event binding with event data + target.bind("mycustomevent", "handlerdata0", handler); + + target.trigger("mycustomevent", "eventdata0", "eventdata1"); + + assertEquals(1, handler.invokationCounter); + assertEquals("handlerdata0", handler.getArgument(0)); + assertEquals("eventdata0", handler.getArgument(1)); + assertEquals("eventdata1", handler.getArgument(2)); + + // unbind + target.unbind("mycustomevent"); + handler.invokationCounter = 0; + target.trigger("mycustomevent"); + assertEquals(0, handler.invokationCounter); + + // test custom event binding with event data as array + target.bind("mycustomevent", new String[]{"handlerdata0", "handlerdata1"}, handler); + + target.trigger("mycustomevent", "eventdata0", "eventdata1"); + + assertEquals(1, handler.invokationCounter); + assertEquals("handlerdata0", handler.getArgument(0)); + assertEquals("handlerdata1", handler.getArgument(1)); + assertEquals("eventdata0", handler.getArgument(2)); + assertEquals("eventdata1", handler.getArgument(3)); + } + + public void testBitlessEventTriggersWithEventName() { + $(e).html("<div id='target'>"); + GQuery target = $("#target", e); + + CounterFunction handler = new CounterFunction(); + + target.bind(Event.ONCLICK, handler); + + target.trigger("click"); + + assertEquals(1, handler.invokationCounter); + } + + public void testDelegationEventWithCustomEvent() { + $(e).html("<div id='container'></div>"); + GQuery container = $("#container", e); + + CounterFunction handler = new CounterFunction(); + + // test custom event binding + $(".custom", e).live("mycustomevent", handler); + + container.html("<div class='custom'></div><div class='other'></div><div" + + " class='custom'>custom2</div>"); + + $(".custom").trigger("mycustomevent"); + + assertEquals(2, handler.invokationCounter); + handler.invokationCounter = 0; + + $(".other").trigger("mycustomevent"); + assertEquals(0, handler.invokationCounter); + + // test custom event unbinding + $(".custom", e).die("mycustomevent"); + + handler.invokationCounter = 0; + + $(".custom").trigger("mycustomevent"); + + assertEquals(0, handler.invokationCounter); + } + + + public void testDelegationEventWithCustomEventAndData() { + $(e).html("<div id='container'></div>"); + GQuery container = $("#container", e); + + CounterFunction handler = new CounterFunction(); + + // test custom event binding + $(".custom", e).live("mycustomevent", new String[]{"handlerdata0", "handlerdata1"}, handler); + + container.html("<div class='custom'></div><div class='other'></div>"); + + $(".custom").trigger("mycustomevent", "eventdata0", "eventdata1"); + + assertEquals(1, handler.invokationCounter); + handler.invokationCounter = 0; + assertEquals("handlerdata0", handler.getArgument(0)); + assertEquals("handlerdata1", handler.getArgument(1)); + assertEquals("eventdata0", handler.getArgument(2)); + assertEquals("eventdata1", handler.getArgument(3)); + + $(".other").trigger("mycustomevent"); + assertEquals(0, handler.invokationCounter); + + // test custom event unbinding + $(".custom", e).die("mycustomevent"); + + handler.invokationCounter = 0; + + $(".custom").trigger("mycustomevent"); + + assertEquals(0, handler.invokationCounter); + } + + public void testDelegationEventWithDelegateAndWithCustomEvent() { + $(e).html("<div id='container'></div>"); + GQuery container = $("#container", e); + + CounterFunction handler = new CounterFunction(); + + // test custom event binding + $(e).delegate(".custom", "mycustomevent", handler); + + container.html("<div class='custom'></div><div class='other'></div>"); + + $(".custom").trigger("mycustomevent"); + + assertEquals(1, handler.invokationCounter); + handler.invokationCounter = 0; + + $(".other").trigger("mycustomevent"); + assertEquals(0, handler.invokationCounter); + + // test custom event unbinding + $(e).undelegate(".custom", "mycustomevent"); + + handler.invokationCounter = 0; + + $(".custom").trigger("mycustomevent"); + + assertEquals(0, handler.invokationCounter); + } + + + public void testDelegationEventWithDelegateAndWithCustomEventAndData() { + $(e).html("<div id='container'></div>"); + GQuery container = $("#container", e); + + CounterFunction handler = new CounterFunction(); + + // test custom event binding + $(e).delegate(".custom", "mycustomevent", new String[]{"handlerdata0", "handlerdata1"}, handler); + + container.html("<div class='custom'></div><div class='other'></div><div class='custom'>custom2</div>"); + + $(".custom").trigger("mycustomevent", "eventdata0", "eventdata1"); + + assertEquals(2, handler.invokationCounter); + handler.invokationCounter = 0; + assertEquals("handlerdata0", handler.getArgument(0)); + assertEquals("handlerdata1", handler.getArgument(1)); + assertEquals("eventdata0", handler.getArgument(2)); + assertEquals("eventdata1", handler.getArgument(3)); + + $(".other").trigger("mycustomevent"); + assertEquals(0, handler.invokationCounter); + + // test custom event unbinding + $(e).undelegate(".custom", "mycustomevent"); + + handler.invokationCounter = 0; + + $(".custom").trigger("mycustomevent"); + + assertEquals(0, handler.invokationCounter); + } + + public void testBitlessEvent() { + $(e).html("<div id='target'>"); + GQuery target = $("#target", e); + + CounterFunction handler = new CounterFunction(); + + // test custom event binding + target.bind("drag", handler); + + target.trigger("drag"); + + assertEquals(1, handler.invokationCounter); + } } |