Events.js 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916
  1. /* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for
  2. * full list of contributors). Published under the Clear BSD license.
  3. * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  4. * full text of the license. */
  5. /**
  6. * @requires OpenLayers/Util.js
  7. */
  8. /**
  9. * Namespace: OpenLayers.Event
  10. * Utility functions for event handling.
  11. */
  12. OpenLayers.Event = {
  13. /**
  14. * Property: observers
  15. * {Object} A hashtable cache of the event observers. Keyed by
  16. * element._eventCacheID
  17. */
  18. observers: false,
  19. /**
  20. * Constant: KEY_BACKSPACE
  21. * {int}
  22. */
  23. KEY_BACKSPACE: 8,
  24. /**
  25. * Constant: KEY_TAB
  26. * {int}
  27. */
  28. KEY_TAB: 9,
  29. /**
  30. * Constant: KEY_RETURN
  31. * {int}
  32. */
  33. KEY_RETURN: 13,
  34. /**
  35. * Constant: KEY_ESC
  36. * {int}
  37. */
  38. KEY_ESC: 27,
  39. /**
  40. * Constant: KEY_LEFT
  41. * {int}
  42. */
  43. KEY_LEFT: 37,
  44. /**
  45. * Constant: KEY_UP
  46. * {int}
  47. */
  48. KEY_UP: 38,
  49. /**
  50. * Constant: KEY_RIGHT
  51. * {int}
  52. */
  53. KEY_RIGHT: 39,
  54. /**
  55. * Constant: KEY_DOWN
  56. * {int}
  57. */
  58. KEY_DOWN: 40,
  59. /**
  60. * Constant: KEY_DELETE
  61. * {int}
  62. */
  63. KEY_DELETE: 46,
  64. /**
  65. * Method: element
  66. * Cross browser event element detection.
  67. *
  68. * Parameters:
  69. * event - {Event}
  70. *
  71. * Returns:
  72. * {DOMElement} The element that caused the event
  73. */
  74. element: function(event) {
  75. return event.target || event.srcElement;
  76. },
  77. /**
  78. * Method: isSingleTouch
  79. * Determine whether event was caused by a single touch
  80. *
  81. * Parameters:
  82. * event - {Event}
  83. *
  84. * Returns:
  85. * {Boolean}
  86. */
  87. isSingleTouch: function(event) {
  88. return event.touches && event.touches.length == 1;
  89. },
  90. /**
  91. * Method: isMultiTouch
  92. * Determine whether event was caused by a multi touch
  93. *
  94. * Parameters:
  95. * event - {Event}
  96. *
  97. * Returns:
  98. * {Boolean}
  99. */
  100. isMultiTouch: function(event) {
  101. return event.touches && event.touches.length > 1;
  102. },
  103. /**
  104. * Method: isLeftClick
  105. * Determine whether event was caused by a left click.
  106. *
  107. * Parameters:
  108. * event - {Event}
  109. *
  110. * Returns:
  111. * {Boolean}
  112. */
  113. isLeftClick: function(event) {
  114. return (((event.which) && (event.which == 1)) ||
  115. ((event.button) && (event.button == 1)));
  116. },
  117. /**
  118. * Method: isRightClick
  119. * Determine whether event was caused by a right mouse click.
  120. *
  121. * Parameters:
  122. * event - {Event}
  123. *
  124. * Returns:
  125. * {Boolean}
  126. */
  127. isRightClick: function(event) {
  128. return (((event.which) && (event.which == 3)) ||
  129. ((event.button) && (event.button == 2)));
  130. },
  131. /**
  132. * Method: stop
  133. * Stops an event from propagating.
  134. *
  135. * Parameters:
  136. * event - {Event}
  137. * allowDefault - {Boolean} If true, we stop the event chain but
  138. * still allow the default browser
  139. * behaviour (text selection, radio-button
  140. * clicking, etc)
  141. * Default false
  142. */
  143. stop: function(event, allowDefault) {
  144. if (!allowDefault) {
  145. if (event.preventDefault) {
  146. event.preventDefault();
  147. } else {
  148. event.returnValue = false;
  149. }
  150. }
  151. if (event.stopPropagation) {
  152. event.stopPropagation();
  153. } else {
  154. event.cancelBubble = true;
  155. }
  156. },
  157. /**
  158. * Method: findElement
  159. *
  160. * Parameters:
  161. * event - {Event}
  162. * tagName - {String}
  163. *
  164. * Returns:
  165. * {DOMElement} The first node with the given tagName, starting from the
  166. * node the event was triggered on and traversing the DOM upwards
  167. */
  168. findElement: function(event, tagName) {
  169. var element = OpenLayers.Event.element(event);
  170. while (element.parentNode && (!element.tagName ||
  171. (element.tagName.toUpperCase() != tagName.toUpperCase()))){
  172. element = element.parentNode;
  173. }
  174. return element;
  175. },
  176. /**
  177. * Method: observe
  178. *
  179. * Parameters:
  180. * elementParam - {DOMElement || String}
  181. * name - {String}
  182. * observer - {function}
  183. * useCapture - {Boolean}
  184. */
  185. observe: function(elementParam, name, observer, useCapture) {
  186. var element = OpenLayers.Util.getElement(elementParam);
  187. useCapture = useCapture || false;
  188. if (name == 'keypress' &&
  189. (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
  190. || element.attachEvent)) {
  191. name = 'keydown';
  192. }
  193. //if observers cache has not yet been created, create it
  194. if (!this.observers) {
  195. this.observers = {};
  196. }
  197. //if not already assigned, make a new unique cache ID
  198. if (!element._eventCacheID) {
  199. var idPrefix = "eventCacheID_";
  200. if (element.id) {
  201. idPrefix = element.id + "_" + idPrefix;
  202. }
  203. element._eventCacheID = OpenLayers.Util.createUniqueID(idPrefix);
  204. }
  205. var cacheID = element._eventCacheID;
  206. //if there is not yet a hash entry for this element, add one
  207. if (!this.observers[cacheID]) {
  208. this.observers[cacheID] = [];
  209. }
  210. //add a new observer to this element's list
  211. this.observers[cacheID].push({
  212. 'element': element,
  213. 'name': name,
  214. 'observer': observer,
  215. 'useCapture': useCapture
  216. });
  217. //add the actual browser event listener
  218. if (element.addEventListener) {
  219. element.addEventListener(name, observer, useCapture);
  220. } else if (element.attachEvent) {
  221. element.attachEvent('on' + name, observer);
  222. }
  223. },
  224. /**
  225. * Method: stopObservingElement
  226. * Given the id of an element to stop observing, cycle through the
  227. * element's cached observers, calling stopObserving on each one,
  228. * skipping those entries which can no longer be removed.
  229. *
  230. * parameters:
  231. * elementParam - {DOMElement || String}
  232. */
  233. stopObservingElement: function(elementParam) {
  234. var element = OpenLayers.Util.getElement(elementParam);
  235. var cacheID = element._eventCacheID;
  236. this._removeElementObservers(OpenLayers.Event.observers[cacheID]);
  237. },
  238. /**
  239. * Method: _removeElementObservers
  240. *
  241. * Parameters:
  242. * elementObservers - {Array(Object)} Array of (element, name,
  243. * observer, usecapture) objects,
  244. * taken directly from hashtable
  245. */
  246. _removeElementObservers: function(elementObservers) {
  247. if (elementObservers) {
  248. for(var i = elementObservers.length-1; i >= 0; i--) {
  249. var entry = elementObservers[i];
  250. var args = new Array(entry.element,
  251. entry.name,
  252. entry.observer,
  253. entry.useCapture);
  254. var removed = OpenLayers.Event.stopObserving.apply(this, args);
  255. }
  256. }
  257. },
  258. /**
  259. * Method: stopObserving
  260. *
  261. * Parameters:
  262. * elementParam - {DOMElement || String}
  263. * name - {String}
  264. * observer - {function}
  265. * useCapture - {Boolean}
  266. *
  267. * Returns:
  268. * {Boolean} Whether or not the event observer was removed
  269. */
  270. stopObserving: function(elementParam, name, observer, useCapture) {
  271. useCapture = useCapture || false;
  272. var element = OpenLayers.Util.getElement(elementParam);
  273. var cacheID = element._eventCacheID;
  274. if (name == 'keypress') {
  275. if ( navigator.appVersion.match(/Konqueror|Safari|KHTML/) ||
  276. element.detachEvent) {
  277. name = 'keydown';
  278. }
  279. }
  280. // find element's entry in this.observers cache and remove it
  281. var foundEntry = false;
  282. var elementObservers = OpenLayers.Event.observers[cacheID];
  283. if (elementObservers) {
  284. // find the specific event type in the element's list
  285. var i=0;
  286. while(!foundEntry && i < elementObservers.length) {
  287. var cacheEntry = elementObservers[i];
  288. if ((cacheEntry.name == name) &&
  289. (cacheEntry.observer == observer) &&
  290. (cacheEntry.useCapture == useCapture)) {
  291. elementObservers.splice(i, 1);
  292. if (elementObservers.length == 0) {
  293. delete OpenLayers.Event.observers[cacheID];
  294. }
  295. foundEntry = true;
  296. break;
  297. }
  298. i++;
  299. }
  300. }
  301. //actually remove the event listener from browser
  302. if (foundEntry) {
  303. if (element.removeEventListener) {
  304. element.removeEventListener(name, observer, useCapture);
  305. } else if (element && element.detachEvent) {
  306. element.detachEvent('on' + name, observer);
  307. }
  308. }
  309. return foundEntry;
  310. },
  311. /**
  312. * Method: unloadCache
  313. * Cycle through all the element entries in the events cache and call
  314. * stopObservingElement on each.
  315. */
  316. unloadCache: function() {
  317. // check for OpenLayers.Event before checking for observers, because
  318. // OpenLayers.Event may be undefined in IE if no map instance was
  319. // created
  320. if (OpenLayers.Event && OpenLayers.Event.observers) {
  321. for (var cacheID in OpenLayers.Event.observers) {
  322. var elementObservers = OpenLayers.Event.observers[cacheID];
  323. OpenLayers.Event._removeElementObservers.apply(this,
  324. [elementObservers]);
  325. }
  326. OpenLayers.Event.observers = false;
  327. }
  328. },
  329. CLASS_NAME: "OpenLayers.Event"
  330. };
  331. /* prevent memory leaks in IE */
  332. OpenLayers.Event.observe(window, 'unload', OpenLayers.Event.unloadCache, false);
  333. // FIXME: Remove this in 3.0. In 3.0, Event.stop will no longer be provided
  334. // by OpenLayers.
  335. if (window.Event) {
  336. OpenLayers.Util.applyDefaults(window.Event, OpenLayers.Event);
  337. } else {
  338. var Event = OpenLayers.Event;
  339. }
  340. /**
  341. * Class: OpenLayers.Events
  342. */
  343. OpenLayers.Events = OpenLayers.Class({
  344. /**
  345. * Constant: BROWSER_EVENTS
  346. * {Array(String)} supported events
  347. */
  348. BROWSER_EVENTS: [
  349. "mouseover", "mouseout",
  350. "mousedown", "mouseup", "mousemove",
  351. "click", "dblclick", "rightclick", "dblrightclick",
  352. "resize", "focus", "blur",
  353. "touchstart", "touchmove", "touchend"
  354. ],
  355. /**
  356. * Property: listeners
  357. * {Object} Hashtable of Array(Function): events listener functions
  358. */
  359. listeners: null,
  360. /**
  361. * Property: object
  362. * {Object} the code object issuing application events
  363. */
  364. object: null,
  365. /**
  366. * Property: element
  367. * {DOMElement} the DOM element receiving browser events
  368. */
  369. element: null,
  370. /**
  371. * Property: eventTypes
  372. * {Array(String)} list of support application events
  373. */
  374. eventTypes: null,
  375. /**
  376. * Property: eventHandler
  377. * {Function} bound event handler attached to elements
  378. */
  379. eventHandler: null,
  380. /**
  381. * APIProperty: fallThrough
  382. * {Boolean}
  383. */
  384. fallThrough: null,
  385. /**
  386. * APIProperty: includeXY
  387. * {Boolean} Should the .xy property automatically be created for browser
  388. * mouse events? In general, this should be false. If it is true, then
  389. * mouse events will automatically generate a '.xy' property on the
  390. * event object that is passed. (Prior to OpenLayers 2.7, this was true
  391. * by default.) Otherwise, you can call the getMousePosition on the
  392. * relevant events handler on the object available via the 'evt.object'
  393. * property of the evt object. So, for most events, you can call:
  394. * function named(evt) {
  395. * this.xy = this.object.events.getMousePosition(evt)
  396. * }
  397. *
  398. * This option typically defaults to false for performance reasons:
  399. * when creating an events object whose primary purpose is to manage
  400. * relatively positioned mouse events within a div, it may make
  401. * sense to set it to true.
  402. *
  403. * This option is also used to control whether the events object caches
  404. * offsets. If this is false, it will not: the reason for this is that
  405. * it is only expected to be called many times if the includeXY property
  406. * is set to true. If you set this to true, you are expected to clear
  407. * the offset cache manually (using this.clearMouseCache()) if:
  408. * the border of the element changes
  409. * the location of the element in the page changes
  410. */
  411. includeXY: false,
  412. /**
  413. * Method: clearMouseListener
  414. * A version of <clearMouseCache> that is bound to this instance so that
  415. * it can be used with <OpenLayers.Event.observe> and
  416. * <OpenLayers.Event.stopObserving>.
  417. */
  418. clearMouseListener: null,
  419. /**
  420. * Constructor: OpenLayers.Events
  421. * Construct an OpenLayers.Events object.
  422. *
  423. * Parameters:
  424. * object - {Object} The js object to which this Events object is being added
  425. * element - {DOMElement} A dom element to respond to browser events
  426. * eventTypes - {Array(String)} Array of custom application events
  427. * fallThrough - {Boolean} Allow events to fall through after these have
  428. * been handled?
  429. * options - {Object} Options for the events object.
  430. */
  431. initialize: function (object, element, eventTypes, fallThrough, options) {
  432. OpenLayers.Util.extend(this, options);
  433. this.object = object;
  434. this.fallThrough = fallThrough;
  435. this.listeners = {};
  436. // keep a bound copy of handleBrowserEvent() so that we can
  437. // pass the same function to both Event.observe() and .stopObserving()
  438. this.eventHandler = OpenLayers.Function.bindAsEventListener(
  439. this.handleBrowserEvent, this
  440. );
  441. // to be used with observe and stopObserving
  442. this.clearMouseListener = OpenLayers.Function.bind(
  443. this.clearMouseCache, this
  444. );
  445. // if eventTypes is specified, create a listeners list for each
  446. // custom application event.
  447. this.eventTypes = [];
  448. if (eventTypes != null) {
  449. for (var i=0, len=eventTypes.length; i<len; i++) {
  450. this.addEventType(eventTypes[i]);
  451. }
  452. }
  453. // if a dom element is specified, add a listeners list
  454. // for browser events on the element and register them
  455. if (element != null) {
  456. this.attachToElement(element);
  457. }
  458. },
  459. /**
  460. * APIMethod: destroy
  461. */
  462. destroy: function () {
  463. if (this.element) {
  464. OpenLayers.Event.stopObservingElement(this.element);
  465. if(this.element.hasScrollEvent) {
  466. OpenLayers.Event.stopObserving(
  467. window, "scroll", this.clearMouseListener
  468. );
  469. }
  470. }
  471. this.element = null;
  472. this.listeners = null;
  473. this.object = null;
  474. this.eventTypes = null;
  475. this.fallThrough = null;
  476. this.eventHandler = null;
  477. },
  478. /**
  479. * APIMethod: addEventType
  480. * Add a new event type to this events object.
  481. * If the event type has already been added, do nothing.
  482. *
  483. * Parameters:
  484. * eventName - {String}
  485. */
  486. addEventType: function(eventName) {
  487. if (!this.listeners[eventName]) {
  488. this.eventTypes.push(eventName);
  489. this.listeners[eventName] = [];
  490. }
  491. },
  492. /**
  493. * Method: attachToElement
  494. *
  495. * Parameters:
  496. * element - {HTMLDOMElement} a DOM element to attach browser events to
  497. */
  498. attachToElement: function (element) {
  499. if(this.element) {
  500. OpenLayers.Event.stopObservingElement(this.element);
  501. }
  502. this.element = element;
  503. for (var i=0, len=this.BROWSER_EVENTS.length; i<len; i++) {
  504. var eventType = this.BROWSER_EVENTS[i];
  505. // every browser event has a corresponding application event
  506. // (whether it's listened for or not).
  507. this.addEventType(eventType);
  508. // use Prototype to register the event cross-browser
  509. OpenLayers.Event.observe(element, eventType, this.eventHandler);
  510. }
  511. // disable dragstart in IE so that mousedown/move/up works normally
  512. OpenLayers.Event.observe(element, "dragstart", OpenLayers.Event.stop);
  513. },
  514. /**
  515. * APIMethod: on
  516. * Convenience method for registering listeners with a common scope.
  517. * Internally, this method calls <register> as shown in the examples
  518. * below.
  519. *
  520. * Example use:
  521. * (code)
  522. * // register a single listener for the "loadstart" event
  523. * events.on({"loadstart": loadStartListener});
  524. *
  525. * // this is equivalent to the following
  526. * events.register("loadstart", undefined, loadStartListener);
  527. *
  528. * // register multiple listeners to be called with the same `this` object
  529. * events.on({
  530. * "loadstart": loadStartListener,
  531. * "loadend": loadEndListener,
  532. * scope: object
  533. * });
  534. *
  535. * // this is equivalent to the following
  536. * events.register("loadstart", object, loadStartListener);
  537. * events.register("loadend", object, loadEndListener);
  538. * (end)
  539. *
  540. * Parameters:
  541. * object - {Object}
  542. */
  543. on: function(object) {
  544. for(var type in object) {
  545. if(type != "scope") {
  546. this.register(type, object.scope, object[type]);
  547. }
  548. }
  549. },
  550. /**
  551. * APIMethod: register
  552. * Register an event on the events object.
  553. *
  554. * When the event is triggered, the 'func' function will be called, in the
  555. * context of 'obj'. Imagine we were to register an event, specifying an
  556. * OpenLayers.Bounds Object as 'obj'. When the event is triggered, the
  557. * context in the callback function will be our Bounds object. This means
  558. * that within our callback function, we can access the properties and
  559. * methods of the Bounds object through the "this" variable. So our
  560. * callback could execute something like:
  561. * : leftStr = "Left: " + this.left;
  562. *
  563. * or
  564. *
  565. * : centerStr = "Center: " + this.getCenterLonLat();
  566. *
  567. * Parameters:
  568. * type - {String} Name of the event to register
  569. * obj - {Object} The object to bind the context to for the callback#.
  570. * If no object is specified, default is the Events's
  571. * 'object' property.
  572. * func - {Function} The callback function. If no callback is
  573. * specified, this function does nothing.
  574. *
  575. *
  576. */
  577. register: function (type, obj, func) {
  578. if ( (func != null) &&
  579. (OpenLayers.Util.indexOf(this.eventTypes, type) != -1) ) {
  580. if (obj == null) {
  581. obj = this.object;
  582. }
  583. var listeners = this.listeners[type];
  584. listeners.push( {obj: obj, func: func} );
  585. }
  586. },
  587. /**
  588. * APIMethod: registerPriority
  589. * Same as register() but adds the new listener to the *front* of the
  590. * events queue instead of to the end.
  591. *
  592. * TODO: get rid of this in 3.0 - Decide whether listeners should be
  593. * called in the order they were registered or in reverse order.
  594. *
  595. *
  596. * Parameters:
  597. * type - {String} Name of the event to register
  598. * obj - {Object} The object to bind the context to for the callback#.
  599. * If no object is specified, default is the Events's
  600. * 'object' property.
  601. * func - {Function} The callback function. If no callback is
  602. * specified, this function does nothing.
  603. */
  604. registerPriority: function (type, obj, func) {
  605. if (func != null) {
  606. if (obj == null) {
  607. obj = this.object;
  608. }
  609. var listeners = this.listeners[type];
  610. if (listeners != null) {
  611. listeners.unshift( {obj: obj, func: func} );
  612. }
  613. }
  614. },
  615. /**
  616. * APIMethod: un
  617. * Convenience method for unregistering listeners with a common scope.
  618. * Internally, this method calls <unregister> as shown in the examples
  619. * below.
  620. *
  621. * Example use:
  622. * (code)
  623. * // unregister a single listener for the "loadstart" event
  624. * events.un({"loadstart": loadStartListener});
  625. *
  626. * // this is equivalent to the following
  627. * events.unregister("loadstart", undefined, loadStartListener);
  628. *
  629. * // unregister multiple listeners with the same `this` object
  630. * events.un({
  631. * "loadstart": loadStartListener,
  632. * "loadend": loadEndListener,
  633. * scope: object
  634. * });
  635. *
  636. * // this is equivalent to the following
  637. * events.unregister("loadstart", object, loadStartListener);
  638. * events.unregister("loadend", object, loadEndListener);
  639. * (end)
  640. */
  641. un: function(object) {
  642. for(var type in object) {
  643. if(type != "scope") {
  644. this.unregister(type, object.scope, object[type]);
  645. }
  646. }
  647. },
  648. /**
  649. * APIMethod: unregister
  650. *
  651. * Parameters:
  652. * type - {String}
  653. * obj - {Object} If none specified, defaults to this.object
  654. * func - {Function}
  655. */
  656. unregister: function (type, obj, func) {
  657. if (obj == null) {
  658. obj = this.object;
  659. }
  660. var listeners = this.listeners[type];
  661. if (listeners != null) {
  662. for (var i=0, len=listeners.length; i<len; i++) {
  663. if (listeners[i].obj == obj && listeners[i].func == func) {
  664. listeners.splice(i, 1);
  665. break;
  666. }
  667. }
  668. }
  669. },
  670. /**
  671. * Method: remove
  672. * Remove all listeners for a given event type. If type is not registered,
  673. * does nothing.
  674. *
  675. * Parameters:
  676. * type - {String}
  677. */
  678. remove: function(type) {
  679. if (this.listeners[type] != null) {
  680. this.listeners[type] = [];
  681. }
  682. },
  683. /**
  684. * APIMethod: triggerEvent
  685. * Trigger a specified registered event.
  686. *
  687. * Parameters:
  688. * type - {String}
  689. * evt - {Event}
  690. *
  691. * Returns:
  692. * {Boolean} The last listener return. If a listener returns false, the
  693. * chain of listeners will stop getting called.
  694. */
  695. triggerEvent: function (type, evt) {
  696. var listeners = this.listeners[type];
  697. // fast path
  698. if(!listeners || listeners.length == 0) {
  699. return undefined;
  700. }
  701. // prep evt object with object & div references
  702. if (evt == null) {
  703. evt = {};
  704. }
  705. evt.object = this.object;
  706. evt.element = this.element;
  707. if(!evt.type) {
  708. evt.type = type;
  709. }
  710. // execute all callbacks registered for specified type
  711. // get a clone of the listeners array to
  712. // allow for splicing during callbacks
  713. listeners = listeners.slice();
  714. var continueChain;
  715. for (var i=0, len=listeners.length; i<len; i++) {
  716. var callback = listeners[i];
  717. // bind the context to callback.obj
  718. continueChain = callback.func.apply(callback.obj, [evt]);
  719. if ((continueChain != undefined) && (continueChain == false)) {
  720. // if callback returns false, execute no more callbacks.
  721. break;
  722. }
  723. }
  724. // don't fall through to other DOM elements
  725. if (!this.fallThrough) {
  726. OpenLayers.Event.stop(evt, true);
  727. }
  728. return continueChain;
  729. },
  730. /**
  731. * Method: handleBrowserEvent
  732. * Basically just a wrapper to the triggerEvent() function, but takes
  733. * care to set a property 'xy' on the event with the current mouse
  734. * position.
  735. *
  736. * Parameters:
  737. * evt - {Event}
  738. */
  739. handleBrowserEvent: function (evt) {
  740. var type = evt.type, listeners = this.listeners[type];
  741. if(!listeners || listeners.length == 0) {
  742. // noone's listening, bail out
  743. return;
  744. }
  745. // add clientX & clientY to all events - corresponds to average x, y
  746. var touches = evt.touches;
  747. if (touches && touches[0]) {
  748. var x = 0;
  749. var y = 0;
  750. var num = touches.length;
  751. var touch;
  752. for (var i=0; i<num; ++i) {
  753. touch = touches[i];
  754. x += touch.clientX;
  755. y += touch.clientY;
  756. }
  757. evt.clientX = x / num;
  758. evt.clientY = y / num;
  759. }
  760. if (this.includeXY) {
  761. evt.xy = this.getMousePosition(evt);
  762. }
  763. this.triggerEvent(type, evt);
  764. },
  765. /**
  766. * APIMethod: clearMouseCache
  767. * Clear cached data about the mouse position. This should be called any
  768. * time the element that events are registered on changes position
  769. * within the page.
  770. */
  771. clearMouseCache: function() {
  772. this.element.scrolls = null;
  773. this.element.lefttop = null;
  774. // OpenLayers.Util.pagePosition needs to use
  775. // element.getBoundingClientRect to correctly calculate the offsets
  776. // for the iPhone, but once the page is scrolled, getBoundingClientRect
  777. // returns incorrect offsets. So our best bet is to not invalidate the
  778. // offsets once we have them, and hope that the page was not scrolled
  779. // when we did the initial calculation.
  780. var body = document.body;
  781. if (body && !((body.scrollTop != 0 || body.scrollLeft != 0) &&
  782. navigator.userAgent.match(/iPhone/i))) {
  783. this.element.offsets = null;
  784. }
  785. },
  786. /**
  787. * Method: getMousePosition
  788. *
  789. * Parameters:
  790. * evt - {Event}
  791. *
  792. * Returns:
  793. * {<OpenLayers.Pixel>} The current xy coordinate of the mouse, adjusted
  794. * for offsets
  795. */
  796. getMousePosition: function (evt) {
  797. if (!this.includeXY) {
  798. this.clearMouseCache();
  799. } else if (!this.element.hasScrollEvent) {
  800. OpenLayers.Event.observe(window, "scroll", this.clearMouseListener);
  801. this.element.hasScrollEvent = true;
  802. }
  803. if (!this.element.scrolls) {
  804. var viewportElement = OpenLayers.Util.getViewportElement();
  805. this.element.scrolls = [
  806. viewportElement.scrollLeft,
  807. viewportElement.scrollTop
  808. ];
  809. }
  810. if (!this.element.lefttop) {
  811. this.element.lefttop = [
  812. (document.documentElement.clientLeft || 0),
  813. (document.documentElement.clientTop || 0)
  814. ];
  815. }
  816. if (!this.element.offsets) {
  817. this.element.offsets = OpenLayers.Util.pagePosition(this.element);
  818. }
  819. return new OpenLayers.Pixel(
  820. (evt.clientX + this.element.scrolls[0]) - this.element.offsets[0]
  821. - this.element.lefttop[0],
  822. (evt.clientY + this.element.scrolls[1]) - this.element.offsets[1]
  823. - this.element.lefttop[1]
  824. );
  825. },
  826. CLASS_NAME: "OpenLayers.Events"
  827. });