You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Events.java 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. /*
  2. * Copyright 2011, The gwtquery team.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
  5. * in compliance with the License. You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software distributed under the License
  10. * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
  11. * or implied. See the License for the specific language governing permissions and limitations under
  12. * the License.
  13. */
  14. package com.google.gwt.query.client.plugins;
  15. import com.google.gwt.dom.client.Element;
  16. import com.google.gwt.dom.client.FormElement;
  17. import com.google.gwt.dom.client.NativeEvent;
  18. import com.google.gwt.dom.client.Node;
  19. import com.google.gwt.query.client.Function;
  20. import com.google.gwt.query.client.GQuery;
  21. import com.google.gwt.query.client.js.JsUtils;
  22. import com.google.gwt.query.client.plugins.events.EventsListener;
  23. import com.google.gwt.query.client.plugins.events.EventsListener.EventName;
  24. import com.google.gwt.user.client.Event;
  25. /**
  26. * GQuery Plugin for handling and queuing browser events.
  27. */
  28. public class Events extends GQuery {
  29. public static final Class<Events> Events = registerPlugin(Events.class, new Plugin<Events>() {
  30. public Events init(GQuery gq) {
  31. return new Events(gq);
  32. }
  33. });
  34. /**
  35. * Don't apply events on text and comment nodes !!
  36. */
  37. private static boolean isEventCapable(Node n) {
  38. return JsUtils.isWindow(n) || JsUtils.isElement(n) && n.getNodeType() != 3
  39. && n.getNodeType() != 8;
  40. }
  41. public Events(GQuery gq) {
  42. super(gq);
  43. }
  44. /**
  45. * Binds a set of handlers to a particular Event for each matched element.
  46. *
  47. * The event handlers are passed as Functions that you can use to prevent default behavior. To
  48. * stop both default action and event bubbling, the function event handler has to return false.
  49. *
  50. * You can pass an additional Object data to your Function as the second parameter
  51. *
  52. */
  53. public Events bind(int eventbits, Object data, Function... funcs) {
  54. for (Element e : elements()) {
  55. if (isEventCapable(e)) {
  56. EventsListener.getInstance(e).bind(eventbits, data, funcs);
  57. }
  58. }
  59. return this;
  60. }
  61. /**
  62. * Binds a set of handlers to a particular Event for each matched element.
  63. *
  64. * The namespace is a way to group events of the same type, making easier unbind specific
  65. * handlers.
  66. *
  67. * The event handlers are passed as Functions that you can use to prevent default behavior. To
  68. * stop both default action and event bubbling, the function event handler has to return false.
  69. *
  70. * You can pass an additional Object data to your Function
  71. *
  72. */
  73. public Events bind(int eventbits, String namespace, Object data, Function... funcs) {
  74. for (Element e : elements()) {
  75. if (isEventCapable(e)) {
  76. EventsListener.getInstance(e).bind(eventbits, namespace, data, funcs);
  77. }
  78. }
  79. return this;
  80. }
  81. /**
  82. * Binds a set of handlers to a particular Event for each matched element.
  83. *
  84. * The name could contain a namespace which is a way to group events of the same type, making
  85. * easier unbind specific handlers.
  86. *
  87. * The event handlers are passed as Functions that you can use to prevent default behavior. To
  88. * stop both default action and event bubbling, the function event handler has to return false.
  89. *
  90. * You can pass an additional Object data to your Function
  91. *
  92. */
  93. public Events bind(String event, Object data, Function... funcs) {
  94. for (Element e : elements()) {
  95. if (isEventCapable(e)) {
  96. EventsListener.getInstance(e).bind(event, data, funcs);
  97. }
  98. }
  99. return this;
  100. }
  101. public GQuery die(int eventbits, String nameSpace) {
  102. EventsListener.getInstance(Element.is(currentContext) ? (Element) currentContext : body).die(
  103. eventbits, nameSpace, null, currentSelector);
  104. return this;
  105. }
  106. public GQuery die(int eventbits) {
  107. return die(eventbits, null);
  108. }
  109. /**
  110. * Remove an event handlers previously attached using live() The selector used with it must match
  111. * exactly the selector initially used with live(). if <code>eventName</code> is null, all event
  112. * handlers corresponding of the GQuery selector will be removed
  113. */
  114. public GQuery die(String eventName) {
  115. EventsListener.getInstance(Element.is(currentContext) ? (Element) currentContext : body).die(
  116. eventName, currentSelector);
  117. return this;
  118. }
  119. public GQuery live(int eventbits, final Object data, Function... funcs) {
  120. return live(eventbits, null, data, funcs);
  121. }
  122. public GQuery live(int eventbits, String nameSpace, final Object data, Function... funcs) {
  123. EventsListener.getInstance(Element.is(currentContext) ? (Element) currentContext : body).live(
  124. eventbits, nameSpace, null, currentSelector, data, funcs);
  125. return this;
  126. }
  127. public GQuery live(String eventName, final Object data, Function... funcs) {
  128. EventsListener.getInstance(Element.is(currentContext) ? (Element) currentContext : body).live(
  129. eventName, currentSelector, data, funcs);
  130. return this;
  131. }
  132. /**
  133. * Bind an event handler to be fired when the mouse enter an element, or trigger that handler on
  134. * an element if no functions are provided.
  135. *
  136. * The mouseenter event differs from mouseover in the way it handles event bubbling. When
  137. * mouseover is used on an element having inner element(s), then when the mouse pointer moves
  138. * hover of the Inner element, the handler would be triggered. This is usually undesirable
  139. * behavior. The mouseenter event, on the other hand, only triggers its handler when the mouse
  140. * enters the element it is bound to, not a descendant.
  141. */
  142. public GQuery mouseenter(Function... f) {
  143. if (f == null || f.length == 0) {
  144. // handle trigger of mouseleave
  145. return triggerHtmlEvent("mouseenter");
  146. }
  147. return bind("mouseenter", null, f);
  148. }
  149. /**
  150. * Bind an event handler to be fired when the mouse leaves an element, or trigger that handler on
  151. * an element if no functions are provided.
  152. *
  153. * The mouseleave event differs from mouseout in the way it handles event bubbling. When mouseout
  154. * is used on an element having inner element(s), then when the mouse pointer moves out of the
  155. * Inner element, the handler would be triggered. This is usually undesirable behavior. The
  156. * mouseleave event, on the other hand, only triggers its handler when the mouse leaves the
  157. * element it is bound to, not a descendant.
  158. */
  159. public GQuery mouseleave(Function... fs) {
  160. if (fs == null || fs.length == 0) {
  161. // handle trigger of mouseleave
  162. return triggerHtmlEvent("mouseleave");
  163. }
  164. return bind("mouseleave", null, fs);
  165. }
  166. /**
  167. * Binds a handler to a particular Event (like Event.ONCLICK) for each matched element. The
  168. * handler is executed only once for each element.
  169. *
  170. * The event handler is passed as a Function that you can use to prevent default behavior. To stop
  171. * both default action and event bubbling, the function event handler has to return false.
  172. *
  173. * You can pass an additional Object data to your Function as the second parameter
  174. */
  175. public Events one(int eventbits, final Object data, final Function f) {
  176. for (Element e : elements()) {
  177. if (isEventCapable(e)) {
  178. EventsListener.getInstance(e).bind(eventbits, data, f, 1);
  179. }
  180. }
  181. return this;
  182. }
  183. /**
  184. * Execute all handlers and behaviors attached to the matched elements for the given event types.
  185. *
  186. * Different event types can be passed joining these using the or bit wise operator.
  187. *
  188. * For keyboard events you can pass a second parameter which represents the key-code of the pushed
  189. * key.
  190. *
  191. * Example: fire(Event.ONCLICK | Event.ONFOCUS) Example: fire(Event.ONKEYDOWN. 'a');
  192. */
  193. public Events trigger(int eventbits, int... keys) {
  194. if ((eventbits | Event.ONBLUR) == Event.ONBLUR)
  195. dispatchEvent(document.createBlurEvent());
  196. if ((eventbits | Event.ONCHANGE) == Event.ONCHANGE)
  197. dispatchEvent(document.createChangeEvent());
  198. if ((eventbits | Event.ONCLICK) == Event.ONCLICK)
  199. dispatchEvent(document.createClickEvent(0, 0, 0, 0, 0, false, false, false, false));
  200. if ((eventbits | Event.ONDBLCLICK) == Event.ONDBLCLICK)
  201. dispatchEvent(document.createDblClickEvent(0, 0, 0, 0, 0, false, false, false, false));
  202. if ((eventbits | Event.ONFOCUS) == Event.ONFOCUS)
  203. dispatchEvent(document.createFocusEvent());
  204. if ((eventbits | Event.ONKEYDOWN) == Event.ONKEYDOWN)
  205. dispatchEvent(document.createKeyDownEvent(false, false, false, false, keys[0]));
  206. if ((eventbits | Event.ONKEYPRESS) == Event.ONKEYPRESS)
  207. dispatchEvent(document.createKeyPressEvent(false, false, false, false, keys[0], 0));
  208. if ((eventbits | Event.ONKEYUP) == Event.ONKEYUP)
  209. dispatchEvent(document.createKeyUpEvent(false, false, false, false, keys[0]));
  210. if ((eventbits | Event.ONLOSECAPTURE) == Event.ONLOSECAPTURE)
  211. triggerHtmlEvent("losecapture");
  212. if ((eventbits | Event.ONMOUSEDOWN) == Event.ONMOUSEDOWN)
  213. dispatchEvent(document.createMouseDownEvent(0, 0, 0, 0, 0, false, false, false, false,
  214. NativeEvent.BUTTON_LEFT));
  215. if ((eventbits | Event.ONMOUSEMOVE) == Event.ONMOUSEMOVE)
  216. dispatchEvent(document.createMouseMoveEvent(0, 0, 0, 0, 0, false, false, false, false,
  217. NativeEvent.BUTTON_LEFT));
  218. if ((eventbits | Event.ONMOUSEOUT) == Event.ONMOUSEOUT)
  219. dispatchEvent(document.createMouseOutEvent(0, 0, 0, 0, 0, false, false, false, false,
  220. NativeEvent.BUTTON_LEFT, null));
  221. if ((eventbits | Event.ONMOUSEOVER) == Event.ONMOUSEOVER)
  222. dispatchEvent(document.createMouseOverEvent(0, 0, 0, 0, 0, false, false, false, false,
  223. NativeEvent.BUTTON_LEFT, null));
  224. if ((eventbits | Event.ONMOUSEUP) == Event.ONMOUSEUP)
  225. dispatchEvent(document.createMouseUpEvent(0, 0, 0, 0, 0, false, false, false, false,
  226. NativeEvent.BUTTON_LEFT));
  227. if ((eventbits | Event.ONSCROLL) == Event.ONSCROLL)
  228. dispatchEvent(document.createScrollEvent());
  229. if ((eventbits | Event.ONERROR) == Event.ONERROR)
  230. dispatchEvent(document.createErrorEvent());
  231. if ((eventbits | Event.ONMOUSEWHEEL) == Event.ONMOUSEWHEEL)
  232. dispatchEvent(document.createMouseEvent("mousewheel", true, true, 0, 0, 0, 0, 0, false,
  233. false, false, false, NativeEvent.BUTTON_LEFT, null));
  234. return this;
  235. }
  236. /**
  237. * Trigger a native event in all matched elements.
  238. *
  239. * @param nativeEvent the browser native event.
  240. * @functions a set of function to run if the event is not canceled.
  241. */
  242. public Events trigger(NativeEvent nativeEvent, Function... fcns) {
  243. dispatchEvent(nativeEvent, null, fcns);
  244. return this;
  245. }
  246. /**
  247. * Trigger a native event in all matched elements.
  248. *
  249. * @param nativeEvent the browser native event.
  250. * @param datas a set of object passed as data when executed the handlers
  251. * @param functions a set of function to run if the event is not canceled.
  252. */
  253. public Events trigger(NativeEvent nativeEvent, Object[] datas, Function... functions) {
  254. dispatchEvent(nativeEvent, datas, functions);
  255. return this;
  256. }
  257. /**
  258. * Trigger a html event in all matched elements.
  259. *
  260. * @param htmlEvent A string representing the desired html event.
  261. * @param functions a set of function to run.
  262. */
  263. public Events triggerHtmlEvent(String htmlEvent, Function... functions) {
  264. return triggerHtmlEvent(htmlEvent, null, functions);
  265. }
  266. /**
  267. * Trigger a html event in all matched elements.
  268. *
  269. * @param htmlEvent An string representing the desired html event.
  270. * @param datas a set of object passed as data when executed the handlers
  271. * @param functions a set of function to run.
  272. */
  273. public Events triggerHtmlEvent(String htmlEvent, Object[] datas, final Function... functions) {
  274. for (EventName part : EventName.split(htmlEvent)) {
  275. NativeEvent e = document.createHtmlEvent(part.eventName, true, true);
  276. JsUtils.prop(e, "namespace", part.nameSpace);
  277. if ("submit".equals(part.eventName)) {
  278. Function submitFunction = new Function() {
  279. public void f(Element e) {
  280. // first submit the form then call the others functions
  281. if (FormElement.is(e)) {
  282. e.<FormElement>cast().submit();
  283. }
  284. callHandlers(e, getEvent(), functions);
  285. }
  286. };
  287. dispatchEvent(e, datas, submitFunction);
  288. } else {
  289. dispatchEvent(e, datas, functions);
  290. }
  291. }
  292. return this;
  293. }
  294. /**
  295. * Removes all handlers, that matches the events bits passed, from each element.
  296. *
  297. * Example: unbind(Event.ONCLICK | Event.ONMOUSEOVER)
  298. */
  299. public Events unbind(int eventbits) {
  300. for (Element e : elements()) {
  301. if (isEventCapable(e)) {
  302. EventsListener.getInstance(e).unbind(eventbits);
  303. }
  304. }
  305. return this;
  306. }
  307. public Events off() {
  308. for (Element e : elements()) {
  309. if (isEventCapable(e)) {
  310. EventsListener.getInstance(e).clean();
  311. }
  312. }
  313. return this;
  314. }
  315. /**
  316. * Removes all handlers, that matches the events bits and the namespace passed, from each element.
  317. *
  318. * Example: unbind(Event.ONCLICK | Event.ONMOUSEOVER, "my.namespace")
  319. */
  320. public Events unbind(int eventbits, String name, Function f) {
  321. for (Element e : elements()) {
  322. if (isEventCapable(e)) {
  323. EventsListener.getInstance(e).unbind(eventbits, name, null, f);
  324. }
  325. }
  326. return this;
  327. }
  328. /**
  329. * Removes all handlers, that matches the event name passed.
  330. *
  331. * This name could contain a namespace.
  332. *
  333. * Example: unbind("click.my.namespace")
  334. */
  335. public Events unbind(String name) {
  336. return unbind(name, null);
  337. }
  338. /**
  339. * Removes the function passed as parameter from the event list matching the event name passed.
  340. * This name could contain a namespace.
  341. *
  342. * Example: unbind("click.my.namespace", myFunction)
  343. */
  344. public Events unbind(String name, Function f) {
  345. for (Element e : elements()) {
  346. if (isEventCapable(e)) {
  347. EventsListener.getInstance(e).unbind(name, f);
  348. }
  349. }
  350. return this;
  351. }
  352. public Events undelegate() {
  353. for (Element e : elements()) {
  354. if (isEventCapable(e)) {
  355. EventsListener.getInstance(e).cleanEventDelegation();
  356. }
  357. }
  358. return this;
  359. }
  360. private void dispatchEvent(NativeEvent evt, Function... funcs) {
  361. dispatchEvent(evt, null, funcs);
  362. }
  363. public void dispatchEvent(NativeEvent evt, Object[] datas, Function... funcs) {
  364. for (Element e : elements()) {
  365. if (isEventCapable(e)) {
  366. $(e).data(EventsListener.EVENT_DATA, datas);
  367. // Ie6-8 don't dispatch bitless event
  368. if ((browser.ie6 || browser.ie8) && Event.getTypeInt(evt.getType()) == -1) {
  369. bubbleEventForIE(e, evt.<Event> cast());
  370. } else {
  371. e.dispatchEvent(evt);
  372. }
  373. if (!JsUtils.isDefaultPrevented(evt)) {
  374. callHandlers(e, evt, funcs);
  375. }
  376. $(e).removeData(EventsListener.EVENT_DATA);
  377. }
  378. }
  379. }
  380. private void bubbleEventForIE(Element e, Event event) {
  381. if (e == null || "html".equalsIgnoreCase(e.getTagName()) || isEventPropagationStopped(event)) {
  382. return;
  383. }
  384. EventsListener.getInstance(e).dispatchEvent(event);
  385. bubbleEventForIE(e.getParentElement(), event);
  386. }
  387. /**
  388. * Only valid for IE6-8.
  389. * @param event
  390. * @return
  391. */
  392. private boolean isEventPropagationStopped(Event event) {
  393. // trick to avoid jnsi
  394. return event.<Element> cast().getPropertyBoolean("cancelBubble");
  395. }
  396. private void callHandlers(Element e, NativeEvent evt, Function... functions) {
  397. for (Function f : functions) {
  398. f.setEvent(Event.as(evt));
  399. f.f(e);
  400. }
  401. }
  402. }