diff options
9 files changed, 354 insertions, 302 deletions
diff --git a/devtest/pom.xml b/devtest/pom.xml index 2ba90061..36100cde 100644 --- a/devtest/pom.xml +++ b/devtest/pom.xml @@ -33,29 +33,12 @@ </dependency> </dependencies> <build> - <resources> - <resource> - <directory>${basedir}/src/main/java</directory> - </resource> - <resource> - <directory>${basedir}/src/main/resources</directory> - </resource> - </resources> - <testResources> - <testResource> - <directory>${basedir}/src/test/java</directory> - </testResource> - <testResource> - <directory>${basedir}/src/test/resources</directory> - </testResource> - </testResources> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>gwt-maven-plugin</artifactId> <version>${gwtmaven}</version> <configuration> - <!-- <style>PRETTY</style> --> <gwtVersion>${gwtversion}</gwtVersion> <modules> <module>com.google.gwt.query.DevTestRunner</module> @@ -73,22 +56,6 @@ </execution> </executions> </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-eclipse-plugin</artifactId> - <configuration> - <downloadSources>true</downloadSources> - <downloadJavadocs>false</downloadJavadocs> - <additionalBuildcommands> - <buildCommand> - <name>com.google.gwt.eclipse.core.gwtProjectValidator</name> - </buildCommand> - </additionalBuildcommands> - <additionalProjectnatures> - <projectnature>com.google.gwt.eclipse.core.gwtNature</projectnature> - </additionalProjectnatures> - </configuration> - </plugin> </plugins> <outputDirectory>${project.build.directory}/${project.build.finalName}/WEB-INF/classes</outputDirectory> </build> diff --git a/devtest/src/main/java/com/google/gwt/query/DevTestRunner.gwt.xml b/devtest/src/main/java/com/google/gwt/query/DevTestRunner.gwt.xml index b3ce809f..373d2f15 100644 --- a/devtest/src/main/java/com/google/gwt/query/DevTestRunner.gwt.xml +++ b/devtest/src/main/java/com/google/gwt/query/DevTestRunner.gwt.xml @@ -2,5 +2,6 @@ <inherits name='com.google.gwt.query.Query'/> <entry-point class='com.google.gwt.query.client.DevTestRunner'/> <set-property name="compiler.useSourceMaps" value="false"/> + <add-linker name="xsiframe"/> </module> 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 098cb2da..3702f010 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 @@ -2503,7 +2503,7 @@ public class GQuery implements Lazy<GQuery, LazyGQuery> { * Since GQuery 1.4.0, this method binds handlers for both mouseenter and mouseleave events. */ public GQuery hover(Function fover, Function fout) { - return bind("mouseenter", null, fover).bind("mouseleave", null, fout); + return bind("mouseenter", fover).bind("mouseleave", fout); } /** @@ -2856,7 +2856,7 @@ public class GQuery implements Lazy<GQuery, LazyGQuery> { */ @Deprecated public GQuery load(Function f) { - return bind(Event.ONLOAD, null, f); + return bind(Event.ONLOAD, f); } /** 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 101ae0b9..7f2127d1 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 @@ -21,8 +21,6 @@ import com.google.gwt.query.client.Function; import com.google.gwt.query.client.GQuery; import com.google.gwt.query.client.js.JsUtils; import com.google.gwt.query.client.plugins.events.EventsListener; -import com.google.gwt.query.client.plugins.events.EventsListener.SpecialEvent; -import com.google.gwt.query.client.plugins.events.GqEvent; import com.google.gwt.user.client.Event; /** @@ -110,7 +108,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, null, currentSelector); + eventbits, nameSpace, null, currentSelector); return this; } @@ -137,7 +135,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, null, currentSelector, data, funcs); + eventbits, nameSpace, null, currentSelector, data, funcs); return this; } @@ -288,18 +286,7 @@ public class Events extends GQuery { * @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; - - String originalEventName = htmlEvent; - String delegateEventName = isSpecialEvent ? specialEvent.getDelegateType() : htmlEvent; - - NativeEvent e = document.createHtmlEvent(delegateEventName, true, true); - - if (isSpecialEvent) { - GqEvent.setOriginalEventType(e, originalEventName); - } - + NativeEvent e = document.createHtmlEvent(htmlEvent, true, true); if ("submit".equals(htmlEvent)){ Function submitFunction = new Function() { public void f(Element e) { @@ -348,7 +335,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, null, f); + EventsListener.getInstance(e).unbind(eventbits, name, null, f); } } return this; diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/LazyEvents.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/LazyEvents.java index 081e1702..98eaf97e 100644 --- a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/LazyEvents.java +++ b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/LazyEvents.java @@ -20,7 +20,6 @@ import com.google.gwt.query.client.Function; import com.google.gwt.query.client.GQuery; import com.google.gwt.query.client.js.JsUtils; import com.google.gwt.query.client.plugins.events.EventsListener; -import com.google.gwt.query.client.plugins.events.EventsListener.SpecialEvent; import com.google.gwt.query.client.plugins.events.GqEvent; import com.google.gwt.user.client.Event; import com.google.gwt.query.client.GQuery.*; 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 5f3cf531..d8b80e55 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 @@ -13,6 +13,8 @@ */ package com.google.gwt.query.client.plugins.events; +import static com.google.gwt.query.client.GQuery.$; + import com.google.gwt.core.client.Duration; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.EventTarget; @@ -24,15 +26,15 @@ import com.google.gwt.query.client.js.JsMap; import com.google.gwt.query.client.js.JsNamedArray; import com.google.gwt.query.client.js.JsObjectArray; import com.google.gwt.query.client.js.JsUtils; +import com.google.gwt.query.client.plugins.events.SpecialEvent.DefaultSpecialEvent; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.EventListener; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; -import static com.google.gwt.query.client.GQuery.$; - /** * This class implements an event queue instance for one Element. The queue instance is configured * as the default event listener in GWT. @@ -45,91 +47,71 @@ import static com.google.gwt.query.client.GQuery.$; */ public class EventsListener implements EventListener { - public interface SpecialEvent { - String getDelegateType(); - - String getOriginalType(); - - Function createDelegateHandler(Function originalHandler); - } - /** * Used for simulating mouseenter and mouseleave events */ - public static class MouseSpecialEvent implements SpecialEvent { - - private String originalType; - private String delegateType; - - public MouseSpecialEvent(String originalType, String delegateType) { - this.originalType = originalType; - this.delegateType = delegateType; - } - - public String getDelegateType() { - return delegateType; - } - - public String getOriginalType() { - return originalType; - } - - public Function createDelegateHandler(Function originalHandler) { - return new SpecialMouseEventHandler(originalHandler); + private static class MouseSpecialEvent extends DefaultSpecialEvent { + public MouseSpecialEvent(final String type, String delegateType) { + super(type, delegateType); + handler = new Function() { + public boolean f(Event e, Object... arg) { + EventTarget eventTarget = e.getCurrentEventTarget(); + Element target = eventTarget != null ? eventTarget.<Element> cast() : null; + + EventTarget relatedEventTarget = e.getRelatedEventTarget(); + Element related = relatedEventTarget != null ? relatedEventTarget.<Element> cast() : null; + + if (related == null || (related != target && !GQuery.contains(target, related))) { + getInstance(target).dispatchEvent(e, type); + } + return true; + }; + }; } } - private interface HandlerWrapper { - Function getOriginalHandler(); - } - private static class SpecialMouseEventHandler extends Function implements HandlerWrapper { - - private Function delegateHandler; - - public SpecialMouseEventHandler(Function originalHandler) { - this.delegateHandler = originalHandler; - } - - @Override - public boolean f(Event e, Object... data) { - EventTarget eventTarget = e.getCurrentEventTarget(); - Element target = eventTarget != null ? eventTarget.<Element> cast() : null; - - EventTarget relatedEventTarget = e.getRelatedEventTarget(); - Element related = relatedEventTarget != null ? relatedEventTarget.<Element> cast() : null; - - // For mousenter/leave call the handler if related is outside the target. - if (related == null || (related != target && !GQuery.contains(target, related))) { - return delegateHandler != null ? delegateHandler.f(e, data) : false; + /** + * Utility class to split a list of events with or without namespaces + */ + private static class EvPart { + String nameSpace; + String eventName; + public EvPart(String n, String e) { + nameSpace = n; + eventName = e; + } + + static List<EvPart> split(String events) { + List<EvPart> ret = new ArrayList<EvPart>(); + String[] parts = events.split("[\\s,]+"); + for (String event : parts) { + String[] tmp = event.split("\\.", 2); + String eventName = tmp[0]; + String nameSpace = tmp.length > 1 ? tmp[1] : null; + ret.add(new EvPart(nameSpace, eventName)); } - - return false; - } - - public Function getOriginalHandler() { - return delegateHandler; + return ret; } } + /** + * The function used per each element event. + */ private static class BindFunction { - Object data; Function function; String nameSpace; - // for special event like mouseleave, mouseenter - String originalEventType; int times; int type; String eventName; - BindFunction(int type, String eventName, String nameSpace, String originalEventType, - Function function, Object data, int times) { + BindFunction(int type, String eventName, String nameSpace, 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 : ""; } @@ -148,7 +130,10 @@ public class EventsListener implements EventListener { } else { arguments = eventData; } - return function.fe(event, arguments); + // FIXME(manolo): figure out when this is null, and fix or comment it. + if (function != null) { + return function.fe(event, arguments); + } } return true; } @@ -157,6 +142,7 @@ public class EventsListener implements EventListener { return type != BITLESS && etype != BITLESS && (type & etype) != 0; } + public boolean isTypeOf(String eName) { return eventName != null && eventName.equalsIgnoreCase(eName); } @@ -184,14 +170,7 @@ public class EventsListener implements EventListener { public boolean isEquals(Function f) { assert f != null : "function f cannot be null"; - Function functionToCompare = - function instanceof HandlerWrapper ? ((HandlerWrapper) function).getOriginalHandler() - : function; - return f.equals(functionToCompare); - } - - public Object getOriginalEventType() { - return originalEventType; + return f.equals(function); } } @@ -204,12 +183,12 @@ public class EventsListener implements EventListener { JsNamedArray<JsObjectArray<BindFunction>> bindFunctionBySelector; LiveBindFunction(String eventName, String namespace, Object data) { - super(BITLESS, eventName, namespace, null, null, data, -1); + super(BITLESS, eventName, namespace, null, data, -1); clean(); } LiveBindFunction(int type, String namespace, Object data) { - super(type, null, namespace, null, null, data, -1); + super(type, null, namespace, null, data, -1); clean(); } @@ -299,8 +278,8 @@ public class EventsListener implements EventListener { /** * Remove the BindFunction associated to this cssSelector */ - public void removeBindFunctionForSelector(String cssSelector, String nameSpace, String originalEventName) { - if (nameSpace == null && originalEventName == null) { + public void removeBindFunctionForSelector(String cssSelector, String nameSpace) { + if (nameSpace == null) { bindFunctionBySelector.delete(cssSelector); } else { JsObjectArray<BindFunction> functions = bindFunctionBySelector.get(cssSelector); @@ -312,10 +291,7 @@ public class EventsListener implements EventListener { for (int i = 0; i < functions.length(); i++) { BindFunction f = functions.get(i); - boolean matchNamespace = nameSpace == null || nameSpace.equals(f.nameSpace); - boolean matchOriginalEventName = originalEventName == null || originalEventName.equals(f.originalEventType); - - if (!matchNamespace || !matchOriginalEventName) { + if (nameSpace != null && !nameSpace.equals(f.nameSpace)) { newFunctions.add(f); } } @@ -378,13 +354,18 @@ public class EventsListener implements EventListener { public static String MOUSEENTER = "mouseenter"; public static String MOUSELEAVE = "mouseleave"; + public static String FOCUSIN = "focusin"; + public static String FOCUSOUT = "focusout"; - public static JsMap<String, SpecialEvent> special; + public static HashMap<String, SpecialEvent> special; static { - special = JsMap.create(); + // Register some special events which already exist in jQuery + special = new HashMap<String, SpecialEvent>(); special.put(MOUSEENTER, new MouseSpecialEvent(MOUSEENTER, "mouseover")); special.put(MOUSELEAVE, new MouseSpecialEvent(MOUSELEAVE, "mouseout")); + special.put(FOCUSIN, new DefaultSpecialEvent(FOCUSIN, "focus")); + special.put(FOCUSOUT, new DefaultSpecialEvent(FOCUSOUT, "blur")); } public static void clean(Element e) { @@ -401,7 +382,7 @@ public class EventsListener implements EventListener { /** * We have to set the gQuery event listener to the element again when - * the element is a widget, because when GWT detaches a widget it removes the + * the element is a widget, because when GWT detaches a widget it removes the * event listener. */ public static void rebind(Element e) { @@ -480,96 +461,59 @@ public class EventsListener implements EventListener { } } - 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(int eventbits, String namespace, Object data, Function function, int times) { + bind(eventbits, namespace, null, data, function, times); } public void bind(String events, final Object data, Function... funcs) { - String[] parts = events.split("[\\s,]+"); - - for (String event : parts) { - - String nameSpace = null; - String eventName = event; - - //seperate possible namespace - //jDramaix: I removed old regex ^([^.]*)\.?(.*$) because it didn't work on IE8... - String[] subparts = event.split("\\.", 2); - - if (subparts.length == 2){ - nameSpace = subparts[1]; - 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; + if (funcs.length == 0 || funcs[0] == null) { + unbind(events, null); + } - int b = Event.getTypeInt(eventName); + for (EvPart ev : EvPart.split(events)) { + SpecialEvent hook = special.get(ev.eventName); + boolean bind = hook == null || hook.setup(element) == false; for (Function function : funcs) { - Function handler = hook != null ? hook.createDelegateHandler(function) : function; - bind(b, nameSpace, eventName, originalEventName, data, handler, -1); + int b = Event.getTypeInt(ev.eventName); + if (bind) { + bind(b, ev.nameSpace, ev.eventName, data, function, -1); + } + if (hook != null) { + hook.add(element, ev.eventName, ev.nameSpace, data, function); + } } } } - 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; - } - + public void bind(int eventbits, String namespace, String eventName, Object data, Function function, int times) { sink(eventbits, eventName); - - elementEvents.add(new BindFunction(eventbits, eventName, namespace, originalEventType, - function, data, times)); + elementEvents.add(new BindFunction(eventbits, eventName, namespace, function, data, times)); } - public void die(String eventNames, String cssSelector) { - String[] parts = eventNames.split("[\\s,]+"); - - for (String event : parts) { - String nameSpace = null; - String eventName = event; - - //seperate possible namespace - //jDramaix: I removed old regex ^([^.]*)\.?(.*$) because it didn't work on IE8... - String[] subparts = event.split("\\.", 2); - - if (subparts.length == 2) { - nameSpace = subparts[1]; - eventName = subparts[0]; + public void die(String events, String cssSelector) { + for (EvPart ev : EvPart.split(events)) { + SpecialEvent hook = special.get(ev.eventName); + boolean unbind = hook == null || hook.tearDown(element) == false; + if (unbind) { + die(Event.getTypeInt(ev.eventName), ev.nameSpace, ev.eventName, cssSelector); + } + if (hook != null) { + hook.remove(element, ev.eventName, ev.nameSpace, null); } - - //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 = Event.getTypeInt(eventName); - - die(b, nameSpace, eventName, originalEventName, cssSelector); } - - } - public void die(int eventbits, String nameSpace, String eventName, String originalEventName, - String cssSelector) { + public void die(int eventbits, String nameSpace, String eventName, String cssSelector) { if (eventbits <= 0) { if (eventName != null && eventName.length() > 0) { LiveBindFunction liveBindFunction = liveBindFunctionByEventName.get(eventName); - maybeRemoveLiveBindFunction(liveBindFunction, cssSelector, BITLESS, eventName, nameSpace, - originalEventName); + maybeRemoveLiveBindFunction(liveBindFunction, cssSelector, BITLESS, eventName, nameSpace); } else { // if eventbits == -1 and eventName is null, remove all event handlers for this selector for (String k : liveBindFunctionByEventType.keys()) { int bits = Integer.parseInt(k); LiveBindFunction liveBindFunction = liveBindFunctionByEventType.get(bits); - maybeRemoveLiveBindFunction(liveBindFunction, cssSelector, bits, null, nameSpace, null); + maybeRemoveLiveBindFunction(liveBindFunction, cssSelector, bits, null, nameSpace); } for (String k : liveBindFunctionByEventName.keys()) { @@ -577,22 +521,20 @@ public class EventsListener implements EventListener { LiveBindFunction liveBindFunction = liveBindFunctionByEventName.get(realKey); if (liveBindFunction != null) { String eName = liveBindFunction.eventName; - maybeRemoveLiveBindFunction(liveBindFunction, cssSelector, BITLESS, eName, - nameSpace, originalEventName); + maybeRemoveLiveBindFunction(liveBindFunction, cssSelector, BITLESS, eName, nameSpace); } } } } else { LiveBindFunction liveBindFunction = liveBindFunctionByEventType.get(eventbits); - maybeRemoveLiveBindFunction(liveBindFunction, cssSelector, eventbits, null, nameSpace, - originalEventName); + maybeRemoveLiveBindFunction(liveBindFunction, cssSelector, eventbits, null, nameSpace); } } private void maybeRemoveLiveBindFunction(LiveBindFunction liveBindFunction, String cssSelector, - int eventbits, String eventName, String nameSpace, String originalEventName) { + int eventbits, String eventName, String nameSpace) { if (liveBindFunction != null) { - liveBindFunction.removeBindFunctionForSelector(cssSelector, nameSpace, originalEventName); + liveBindFunction.removeBindFunctionForSelector(cssSelector, nameSpace); if (liveBindFunction.isEmpty()){ if (eventbits != BITLESS) { liveBindFunctionByEventType.remove(eventbits); @@ -603,16 +545,23 @@ public class EventsListener implements EventListener { } } + /** + * Dispatch an event in this element. + */ public void dispatchEvent(Event event) { - String ename = event.getType(); - int etype = Event.getTypeInt(ename); - String originalEventType = GqEvent.getOriginalEventType(event); - Object[] handlerData = $(element).data(EVENT_DATA); + dispatchEvent(event, event.getType()); + } + /** + * Dispatch an event in this element but changing the type, + * it's useful for special events. + */ + public void dispatchEvent(Event event, String eventName) { + int etype = Event.getTypeInt(eventName); + 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) || listener.isTypeOf(ename)) - && (originalEventType == null || originalEventType.equals(listener.getOriginalEventType()))) { + if (listener != null && (listener.hasEventType(etype) || listener.isTypeOf(eventName))) { if (!listener.fire(event, handlerData)) { event.stopPropagation(); event.preventDefault(); @@ -630,46 +579,32 @@ public class EventsListener implements EventListener { } public void live(String events, String cssSelector, Object data, Function... funcs) { - - String[] parts = events.split("[\\s,]+"); - - for (String event : parts) { - - String nameSpace = null; - String eventName = event; - - - String[] subparts = event.split("\\.", 2); - - if (subparts.length == 2) { - nameSpace = subparts[1]; - 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 = Event.getTypeInt(eventName); + for (EvPart ev : EvPart.split(events)) { + SpecialEvent hook = special.get(ev.eventName); + boolean bind = hook == null || hook.setup(element) == false; for (Function function : funcs) { - Function handler = hook != null ? hook.createDelegateHandler(function) : function; - live(b, nameSpace, eventName, originalEventName, cssSelector, data, handler); + int b = Event.getTypeInt(ev.eventName); + if (bind) { + live(b, ev.nameSpace, ev.eventName, cssSelector, data, function); + } + if (hook != null) { + hook.add(element, ev.eventName, ev.nameSpace, data, function); + } } } } - public void live(int eventbits, String nameSpace, String eventName, String originalEventName, - String cssSelector, Object data, Function... funcs) { + public void live(int eventbits, String nameSpace, String eventName, String cssSelector, + Object data, Function... funcs) { if (eventbits != BITLESS) { - liveBitEvent(eventbits, nameSpace, originalEventName, cssSelector, data, funcs); + liveBitEvent(eventbits, nameSpace, cssSelector, data, funcs); } else { - liveBitlessEvent(eventName, nameSpace, originalEventName, cssSelector, data, funcs); + liveBitlessEvent(eventName, nameSpace, cssSelector, data, funcs); } } - private void liveBitlessEvent(String eventName, String nameSpace, String originalEventName, - String cssSelector, Object data, Function... funcs) { + private void liveBitlessEvent(String eventName, String nameSpace, String cssSelector, + Object data, Function... funcs) { LiveBindFunction liveBindFunction = liveBindFunctionByEventName.get(eventName); if (liveBindFunction == null) { @@ -681,12 +616,12 @@ public class EventsListener implements EventListener { for (Function f : funcs) { liveBindFunction.addBindFunctionForSelector(cssSelector, new BindFunction(BITLESS, eventName, - nameSpace, originalEventName, f, data, -1)); + nameSpace, f, data, -1)); } } - private void liveBitEvent(int eventbits, String nameSpace, String originalEventName, - String cssSelector, Object data, Function... funcs) { + private void liveBitEvent(int eventbits, String nameSpace, String cssSelector, Object data, + Function... funcs) { for (int i = 0; i < 28; i++) { int event = (int) Math.pow(2, i); if ((eventbits & event) == event) { @@ -702,7 +637,7 @@ public class EventsListener implements EventListener { for (Function f : funcs) { liveBindFunction.addBindFunctionForSelector(cssSelector, new BindFunction(event, - null, nameSpace, originalEventName, f, data, -1)); + null, nameSpace, f, data, -1)); } } } @@ -727,68 +662,58 @@ public class EventsListener implements EventListener { } public void unbind(int eventbits) { - unbind(eventbits, null, null, null, null); + unbind(eventbits, null, null, null); } - public void unbind(int eventbits, String namespace, String eventName, String originalEventType, - Function f) { - + public void unbind(int eventbits, String namespace, String eventName, Function f) { JsObjectArray<BindFunction> newList = JsObjectArray.createArray().cast(); for (int i = 0; i < elementEvents.length(); i++) { BindFunction listener = elementEvents.get(i); - boolean matchNS = isNullOrEmpty(namespace) || listener.nameSpace.equals(namespace); boolean matchEV = eventbits <= 0 || listener.hasEventType(eventbits); boolean matchEVN = matchEV || listener.isTypeOf(eventName); - boolean matchOEVT = (isNullOrEmpty(eventName) && !isNullOrEmpty(namespace) && matchNS) - || (originalEventType == null && listener.getOriginalEventType() == null) - || (originalEventType != null && originalEventType.equals(listener.getOriginalEventType())); boolean matchFC = f == null || listener.isEquals(f); - if (matchNS && matchEV && matchEVN && matchFC && matchOEVT) { + if (matchNS && matchEV && matchEVN && matchFC) { int currentEventbits = listener.unsink(eventbits); - if (currentEventbits == 0) { // the BindFunction doesn't listen anymore on any events continue; } } - newList.add(listener); } - elementEvents = newList; + elementEvents = newList; + } + /** + * Return true if the element is listening for the + * given eventBit or eventName. + */ + public boolean hasHandlers(int eventBits, String eventName) { + for (int i = 0, j = elementEvents.length(); i < j; i++) { + BindFunction function = elementEvents.get(i); + if (function.hasEventType(eventBits) || function.isTypeOf(eventName)) { + return true; + } + } + return false; } - private boolean isNullOrEmpty(String s) { + private static boolean isNullOrEmpty(String s) { return s == null || s.isEmpty(); } public void unbind(String events, Function f) { - - String[] parts = events.split("[\\s,]+"); - - for (String event : parts) { - String nameSpace = null; - String eventName = event; - - //seperate possible namespace - //jDramaix: I removed old regex ^([^.]*)\.?(.*$) because it didn't work on IE8... - String[] subparts = event.split("\\.", 2); - - if (subparts.length == 2){ - nameSpace = subparts[1]; - eventName = subparts[0]; + for (EvPart ev : EvPart.split(events)) { + SpecialEvent hook = special.get(ev.eventName); + boolean unbind = hook == null || hook.tearDown(element) == false; + if (unbind) { + unbind(Event.getTypeInt(ev.eventName), ev.nameSpace, ev.eventName, f); + } + if (hook != null) { + hook.remove(element, ev.eventName, ev.nameSpace, f); } - - //handle special event - SpecialEvent hook = special.get(eventName); - eventName = hook != null ? hook.getDelegateType() : eventName; - String originalEventName = hook != null ? hook.getOriginalType() : null; - - int b = Event.getTypeInt(eventName); - - unbind(b, nameSpace, eventName, originalEventName, f); } } diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/events/GqEvent.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/events/GqEvent.java index 1a92374f..e98a5f4c 100644 --- a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/events/GqEvent.java +++ b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/events/GqEvent.java @@ -1,7 +1,6 @@ package com.google.gwt.query.client.plugins.events; import com.google.gwt.dom.client.Element; -import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.query.client.GQuery; import com.google.gwt.user.client.Event; @@ -23,14 +22,6 @@ import com.google.gwt.user.client.Event; */ public class GqEvent extends Event { - public static native void setOriginalEventType(NativeEvent evt, String originalEventName)/*-{ - evt["__gwtquery_originalEventName"] = originalEventName; - }-*/; - - public static native String getOriginalEventType(Event evt)/*-{ - return evt["__gwtquery_originalEventName"] || null; - }-*/; - // Gwt Events class has not this event defined, // so we have to select one power of 2 which is unused in Event class public static int ONSUBMIT = 0x10000000; diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/events/SpecialEvent.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/events/SpecialEvent.java new file mode 100644 index 00000000..cc4020b9 --- /dev/null +++ b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/events/SpecialEvent.java @@ -0,0 +1,115 @@ +/* + * Copyright 2014, The gwtquery 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.events; + +import com.google.gwt.dom.client.Element; +import com.google.gwt.query.client.Function; +import com.google.gwt.user.client.Event; + +/** + * Interface used to register special events. + * + * Use EventsListeners.special.add( + */ +public interface SpecialEvent { + + /** + * Default implementation of SpecialEvents for simple cases like + * creating event aliases. Extend this for creating more complex + * cases. + */ + public static class DefaultSpecialEvent implements SpecialEvent { + protected final String delegateType; + protected final String type; + + protected Function handler = new Function() { + public boolean f(Event e, Object... arg) { + setEvent(e); + EventsListener.getInstance(getElement()).dispatchEvent(e, type); + return true; + }; + }; + + public DefaultSpecialEvent(String type, String delegateType) { + this.type = type; + this.delegateType = delegateType; + } + + protected EventsListener listener(Element e) { + return EventsListener.getInstance(e); + } + + @Override + public void add(Element e, String eventType, String nameSpace, Object data, Function f) { + // Nothing to do, let gQuery use default events mechanism + } + + @Override + public boolean hasHandlers(Element e) { + return listener(e).hasHandlers(Event.getTypeInt(type), type); + } + + @Override + public void remove(Element e, String eventType, String nameSpace, Function f) { + // Nothing to do, let gQuery use default events mechanism + } + + @Override + public boolean setup(Element e) { + if (!hasHandlers(e)) { + listener(e).bind(delegateType, null, handler); + } + return false; + } + + @Override + public boolean tearDown(Element e) { + if (!hasHandlers(e)) { + listener(e).unbind(delegateType, handler); + } + return false; + } + } + + /** + * For each bind call the add function is called. + */ + void add(Element e, String eventType, String nameSpace, Object data, Function f); + + /** + * Return true if there are handlers bound to this special event. + */ + boolean hasHandlers(Element e); + + /** + * For each unbind call the remove function is called. + */ + void remove(Element e, String eventType, String nameSpace, Function f); + + /** + * When the first event handler is bound for an EventsListener gQuery executes the setup function. + * + * If the method returns false means that gQuery has to run the default bind for the event before + * calling add. + */ + boolean setup(Element e); + + /** + * The last unbind call triggers the tearDown method. + * + * If the method returns false means that gQuery has to run the default unbind for the event + * before calling remove. + */ + boolean tearDown(Element e); +}
\ No newline at end of file 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 98700235..08777f32 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 @@ -383,7 +383,7 @@ public class GQueryEventsTestGwt extends GWTTestCase { $("p", e).click(); assertEquals("white", $("p", e).css("color", false)); - // hover (mouseover, mouseout) + // hover (mouseenter, mouseleave) $("p", e).hover(new Function() { public void f(Element elem) { $(elem).css(CSS.BACKGROUND_COLOR.with(RGBColor.YELLOW)); @@ -398,6 +398,11 @@ public class GQueryEventsTestGwt extends GWTTestCase { $("p", e).trigger(Event.ONMOUSEOUT); assertEquals("white", $("p", e).css("background-color", false)); + $("p", e).css(CSS.COLOR.with(RGBColor.WHITE)); + $("p", e).hover(null, null); + $("p", e).trigger(Event.ONMOUSEOVER); + assertEquals("white", $("p", e).css("background-color", false)); + // key events $(e).html("<input type='text'/>"); Function keyEventAction = new Function() { @@ -450,6 +455,28 @@ public class GQueryEventsTestGwt extends GWTTestCase { assertEquals("", $("p", e).css("border", false)); } + @DoNotRunWith({Platform.HtmlUnitLayout}) + public void testSpecialFocusInOut() { + $(e).html("<p>Content</p>"); + $("p", e).on(EventsListener.FOCUSIN, new Function() { + public void f(Element elem) { + GQuery.console.log("focus"); + $(elem).css("background-color", "red"); + } + }); + $("p", e).focus(); + assertEquals("red", $("p", e).css("background-color", false)); + + // blur + $("p", e).on(EventsListener.FOCUSOUT, new Function() { + public void f(Element elem) { + $(elem).css("background-color", "white"); + } + }); + $("p", e).blur(); + assertEquals("white", $("p", e).css("background-color", false)); + } + public void testLazyMethods() { $(e).css(CSS.COLOR.with(RGBColor.WHITE)); assertEquals("white", $(e).css("color", false)); @@ -566,6 +593,46 @@ public class GQueryEventsTestGwt extends GWTTestCase { } + public void testLiveWithSpecial() { + $(e).html("<div id='div1'><div id='div2'>Content 1<span id='span1'> blop</span></div></div>"); + + $(".clickable", e).live("mouseenter", new Function() { + public void f(Element e) { + $(e).css(CSS.COLOR.with(RGBColor.RED)); + } + }); + + $("#div1", e).addClass("clickable"); + $("#span1", e).mouseenter(); + assertEquals("red", $("#div1", e).css(CSS.COLOR, false)); + + $(".clickable", e).die("mouseenter"); + $("*", e).css(CSS.COLOR.with(RGBColor.BLACK)); + + $("#span1", e).mouseenter(); + assertEquals("black", $("#div1", e).css(CSS.COLOR, false)); + } + + public void testOnOffWithSpecial() { + $(e).html("<div id='div1'><div id='div2'>Content 1<span id='span1'> blop</span></div></div>"); + + $(e).on("mouseenter", ".clickable", new Function() { + public void f(Element e) { + $(e).css(CSS.COLOR.with(RGBColor.RED)); + } + }); + + $("#div1", e).addClass("clickable"); + $("#span1", e).mouseenter(); + assertEquals("red", $("#div1", e).css(CSS.COLOR, false)); + + $(e).off("mouseenter", ".clickable"); + $("*", e).css(CSS.COLOR.with(RGBColor.BLACK)); + + $("#span1", e).mouseenter(); + assertEquals("black", $("#div1", e).css(CSS.COLOR, false)); + } + public void testLiveWithMultipleEvent() { $(e).html("<div id='div1'><div id='div2'>Content 1<span id='span1'> blop</span></div></div>"); @@ -1198,10 +1265,10 @@ public class GQueryEventsTestGwt extends GWTTestCase { $(".mainDiv", e).click(); assertEquals("red", $(".mainDiv", e).css("color", false)); - + // reset $(".mainDiv", e).css("color", "black"); - + $(".mainDiv", e).off("click"); $(".mainDiv", e).click(); @@ -1219,7 +1286,7 @@ public class GQueryEventsTestGwt extends GWTTestCase { $(".mainDiv", e).click(); assertEquals("red", $(".mainDiv", e).css("color", false)); - + // reset $(".mainDiv", e).css("color", "black"); @@ -1273,7 +1340,7 @@ public class GQueryEventsTestGwt extends GWTTestCase { assertEquals("white", $(".mainDiv", e).css("background-color", false)); } - + public void testOnOffWithSelector() { $(e).html("<div class='mainDiv'><div class='subDiv'>Content " + "0<span>blop</span></div></div><div class='mainDiv'><div class='subDiv'>" + @@ -1285,7 +1352,7 @@ public class GQueryEventsTestGwt extends GWTTestCase { $(e).css("color", "red"); } }); - + for (Element mainDiv : $(".mainDiv", e).elements()) { for (int i = 0; i < 3; i++) { @@ -1436,10 +1503,10 @@ public class GQueryEventsTestGwt extends GWTTestCase { $(b).trigger("custom"); assertEquals("200px", $("button").css("width", false)); } - + public void testIssue152() { $(e).html("<div class='mdiv'>"); - final GQuery div = $(".mdiv", e); + final GQuery div = $(".mdiv", e); final int[] count = { 0 }; div.one(Event.ONCLICK, null, new Function() { public void f() { |