eventManager.js 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. 'use strict';
  2. exports.__esModule = true;
  3. var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); // import Core from './core';
  4. exports.getListenersCounter = getListenersCounter;
  5. var _element = require('./helpers/dom/element');
  6. var _feature = require('./helpers/feature');
  7. var _event = require('./helpers/dom/event');
  8. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  9. /**
  10. * Counter which tracks unregistered listeners (useful for detecting memory leaks).
  11. *
  12. * @type {Number}
  13. */
  14. var listenersCounter = 0;
  15. /**
  16. * Event DOM manager for internal use in Handsontable.
  17. *
  18. * @class EventManager
  19. * @util
  20. */
  21. var EventManager = function () {
  22. /**
  23. * @param {Object} [context=null]
  24. * @private
  25. */
  26. function EventManager() {
  27. var context = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
  28. _classCallCheck(this, EventManager);
  29. this.context = context || this;
  30. if (!this.context.eventListeners) {
  31. this.context.eventListeners = [];
  32. }
  33. }
  34. /**
  35. * Register specified listener (`eventName`) to the element.
  36. *
  37. * @param {Element} element Target element.
  38. * @param {String} eventName Event name.
  39. * @param {Function} callback Function which will be called after event occur.
  40. * @returns {Function} Returns function which you can easily call to remove that event
  41. */
  42. _createClass(EventManager, [{
  43. key: 'addEventListener',
  44. value: function addEventListener(element, eventName, callback) {
  45. var _this = this;
  46. var context = this.context;
  47. function callbackProxy(event) {
  48. event = extendEvent(context, event);
  49. callback.call(this, event);
  50. }
  51. this.context.eventListeners.push({
  52. element: element,
  53. event: eventName,
  54. callback: callback,
  55. callbackProxy: callbackProxy
  56. });
  57. if (window.addEventListener) {
  58. element.addEventListener(eventName, callbackProxy, false);
  59. } else {
  60. element.attachEvent('on' + eventName, callbackProxy);
  61. }
  62. listenersCounter++;
  63. return function () {
  64. _this.removeEventListener(element, eventName, callback);
  65. };
  66. }
  67. /**
  68. * Remove the event listener previously registered.
  69. *
  70. * @param {Element} element Target element.
  71. * @param {String} eventName Event name.
  72. * @param {Function} callback Function to remove from the event target. It must be the same as during registration listener.
  73. */
  74. }, {
  75. key: 'removeEventListener',
  76. value: function removeEventListener(element, eventName, callback) {
  77. var len = this.context.eventListeners.length;
  78. var tmpEvent = void 0;
  79. while (len--) {
  80. tmpEvent = this.context.eventListeners[len];
  81. if (tmpEvent.event == eventName && tmpEvent.element == element) {
  82. if (callback && callback != tmpEvent.callback) {
  83. /* eslint-disable no-continue */
  84. continue;
  85. }
  86. this.context.eventListeners.splice(len, 1);
  87. if (tmpEvent.element.removeEventListener) {
  88. tmpEvent.element.removeEventListener(tmpEvent.event, tmpEvent.callbackProxy, false);
  89. } else {
  90. tmpEvent.element.detachEvent('on' + tmpEvent.event, tmpEvent.callbackProxy);
  91. }
  92. listenersCounter--;
  93. }
  94. }
  95. }
  96. /**
  97. * Clear all previously registered events.
  98. *
  99. * @private
  100. * @since 0.15.0-beta3
  101. */
  102. }, {
  103. key: 'clearEvents',
  104. value: function clearEvents() {
  105. if (!this.context) {
  106. return;
  107. }
  108. var len = this.context.eventListeners.length;
  109. while (len--) {
  110. var event = this.context.eventListeners[len];
  111. if (event) {
  112. this.removeEventListener(event.element, event.event, event.callback);
  113. }
  114. }
  115. }
  116. /**
  117. * Clear all previously registered events.
  118. */
  119. }, {
  120. key: 'clear',
  121. value: function clear() {
  122. this.clearEvents();
  123. }
  124. /**
  125. * Destroy instance of EventManager.
  126. */
  127. }, {
  128. key: 'destroy',
  129. value: function destroy() {
  130. this.clearEvents();
  131. this.context = null;
  132. }
  133. /**
  134. * Trigger event at the specified target element.
  135. *
  136. * @param {Element} element Target element.
  137. * @param {String} eventName Event name.
  138. */
  139. }, {
  140. key: 'fireEvent',
  141. value: function fireEvent(element, eventName) {
  142. var options = {
  143. bubbles: true,
  144. cancelable: eventName !== 'mousemove',
  145. view: window,
  146. detail: 0,
  147. screenX: 0,
  148. screenY: 0,
  149. clientX: 1,
  150. clientY: 1,
  151. ctrlKey: false,
  152. altKey: false,
  153. shiftKey: false,
  154. metaKey: false,
  155. button: 0,
  156. relatedTarget: undefined
  157. };
  158. var event;
  159. if (document.createEvent) {
  160. event = document.createEvent('MouseEvents');
  161. event.initMouseEvent(eventName, options.bubbles, options.cancelable, options.view, options.detail, options.screenX, options.screenY, options.clientX, options.clientY, options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.button, options.relatedTarget || document.body.parentNode);
  162. } else {
  163. event = document.createEventObject();
  164. }
  165. if (element.dispatchEvent) {
  166. element.dispatchEvent(event);
  167. } else {
  168. element.fireEvent('on' + eventName, event);
  169. }
  170. }
  171. }]);
  172. return EventManager;
  173. }();
  174. /**
  175. * @param {Object} context
  176. * @param {Event} event
  177. * @private
  178. * @returns {*}
  179. */
  180. function extendEvent(context, event) {
  181. var componentName = 'HOT-TABLE';
  182. var isHotTableSpotted = void 0;
  183. var fromElement = void 0;
  184. var realTarget = void 0;
  185. var target = void 0;
  186. var len = void 0;
  187. var nativeStopImmediatePropagation = void 0;
  188. event.isTargetWebComponent = false;
  189. event.realTarget = event.target;
  190. nativeStopImmediatePropagation = event.stopImmediatePropagation;
  191. event.stopImmediatePropagation = function () {
  192. nativeStopImmediatePropagation.apply(this);
  193. (0, _event.stopImmediatePropagation)(this);
  194. };
  195. if (!EventManager.isHotTableEnv) {
  196. return event;
  197. }
  198. event = (0, _element.polymerWrap)(event);
  199. len = event.path ? event.path.length : 0;
  200. while (len--) {
  201. if (event.path[len].nodeName === componentName) {
  202. isHotTableSpotted = true;
  203. } else if (isHotTableSpotted && event.path[len].shadowRoot) {
  204. target = event.path[len];
  205. break;
  206. }
  207. if (len === 0 && !target) {
  208. target = event.path[len];
  209. }
  210. }
  211. if (!target) {
  212. target = event.target;
  213. }
  214. event.isTargetWebComponent = true;
  215. if ((0, _feature.isWebComponentSupportedNatively)()) {
  216. event.realTarget = event.srcElement || event.toElement;
  217. } else if (context instanceof Core || context instanceof Walkontable) {
  218. // Polymer doesn't support `event.target` property properly we must emulate it ourselves
  219. if (context instanceof Core) {
  220. fromElement = context.view ? context.view.wt.wtTable.TABLE : null;
  221. } else if (context instanceof Walkontable) {
  222. // .wtHider
  223. fromElement = context.wtTable.TABLE.parentNode.parentNode;
  224. }
  225. realTarget = (0, _element.closest)(event.target, [componentName], fromElement);
  226. if (realTarget) {
  227. event.realTarget = fromElement.querySelector(componentName) || event.target;
  228. } else {
  229. event.realTarget = event.target;
  230. }
  231. }
  232. Object.defineProperty(event, 'target', {
  233. get: function get() {
  234. return (0, _element.polymerWrap)(target);
  235. },
  236. enumerable: true,
  237. configurable: true
  238. });
  239. return event;
  240. }
  241. exports.default = EventManager;
  242. function getListenersCounter() {
  243. return listenersCounter;
  244. };