Version 3.17.2
Show:

File: widget/js/WidgetUIEvents.js

  1. /**
  2. * Support for Widget UI Events (Custom Events fired by the widget, which wrap the underlying DOM events - e.g. widget:click, widget:mousedown)
  3. *
  4. * @module widget
  5. * @submodule widget-uievents
  6. */
  7.  
  8. var BOUNDING_BOX = "boundingBox",
  9. Widget = Y.Widget,
  10. RENDER = "render",
  11. L = Y.Lang,
  12. EVENT_PREFIX_DELIMITER = ":",
  13.  
  14. // Map of Node instances serving as a delegation containers for a specific
  15. // event type to Widget instances using that delegation container.
  16. _uievts = Y.Widget._uievts = Y.Widget._uievts || {};
  17.  
  18. Y.mix(Widget.prototype, {
  19.  
  20. /**
  21. * Destructor logic for UI event infrastructure,
  22. * invoked during Widget destruction.
  23. *
  24. * @method _destroyUIEvents
  25. * @for Widget
  26. * @private
  27. */
  28. _destroyUIEvents: function() {
  29.  
  30. var widgetGuid = Y.stamp(this, true);
  31.  
  32. Y.each(_uievts, function (info, key) {
  33. if (info.instances[widgetGuid]) {
  34. // Unregister this Widget instance as needing this delegated
  35. // event listener.
  36. delete info.instances[widgetGuid];
  37.  
  38. // There are no more Widget instances using this delegated
  39. // event listener, so detach it.
  40.  
  41. if (Y.Object.isEmpty(info.instances)) {
  42. info.handle.detach();
  43.  
  44. if (_uievts[key]) {
  45. delete _uievts[key];
  46. }
  47. }
  48. }
  49. });
  50. },
  51.  
  52. /**
  53. * Map of DOM events that should be fired as Custom Events by the
  54. * Widget instance.
  55. *
  56. * @property UI_EVENTS
  57. * @for Widget
  58. * @type Object
  59. */
  60. UI_EVENTS: Y.Node.DOM_EVENTS,
  61.  
  62. /**
  63. * Returns the node on which to bind delegate listeners.
  64. *
  65. * @method _getUIEventNode
  66. * @for Widget
  67. * @protected
  68. */
  69. _getUIEventNode: function () {
  70. return this.get(BOUNDING_BOX);
  71. },
  72.  
  73. /**
  74. * Binds a delegated DOM event listener of the specified type to the
  75. * Widget's outtermost DOM element to facilitate the firing of a Custom
  76. * Event of the same type for the Widget instance.
  77. *
  78. * @method _createUIEvent
  79. * @for Widget
  80. * @param type {String} String representing the name of the event
  81. * @private
  82. */
  83. _createUIEvent: function (type) {
  84.  
  85. var uiEvtNode = this._getUIEventNode(),
  86. key = (Y.stamp(uiEvtNode) + type),
  87. info = _uievts[key],
  88. handle;
  89.  
  90. // For each Node instance: Ensure that there is only one delegated
  91. // event listener used to fire Widget UI events.
  92.  
  93. if (!info) {
  94.  
  95. handle = uiEvtNode.delegate(type, function (evt) {
  96.  
  97. var widget = Widget.getByNode(this);
  98.  
  99. // Widget could be null if node instance belongs to
  100. // another Y instance.
  101.  
  102. if (widget) {
  103. if (widget._filterUIEvent(evt)) {
  104. widget.fire(evt.type, { domEvent: evt });
  105. }
  106. }
  107.  
  108. }, "." + Y.Widget.getClassName());
  109.  
  110. _uievts[key] = info = { instances: {}, handle: handle };
  111. }
  112.  
  113. // Register this Widget as using this Node as a delegation container.
  114. info.instances[Y.stamp(this)] = 1;
  115. },
  116.  
  117. /**
  118. * This method is used to determine if we should fire
  119. * the UI Event or not. The default implementation makes sure
  120. * that for nested delegates (nested unrelated widgets), we don't
  121. * fire the UI event listener more than once at each level.
  122. *
  123. * <p>For example, without the additional filter, if you have nested
  124. * widgets, each widget will have a delegate listener. If you
  125. * click on the inner widget, the inner delegate listener's
  126. * filter will match once, but the outer will match twice
  127. * (based on delegate's design) - once for the inner widget,
  128. * and once for the outer.</p>
  129. *
  130. * @method _filterUIEvent
  131. * @for Widget
  132. * @param {DOMEventFacade} evt
  133. * @return {boolean} true if it's OK to fire the custom UI event, false if not.
  134. * @private
  135. *
  136. */
  137. _filterUIEvent: function(evt) {
  138. // Either it's hitting this widget's delegate container (and not some other widget's),
  139. // or the container it's hitting is handling this widget's ui events.
  140. return (evt.currentTarget.compareTo(evt.container) || evt.container.compareTo(this._getUIEventNode()));
  141. },
  142.  
  143. /**
  144. * Determines if the specified event is a UI event.
  145. *
  146. * @private
  147. * @method _isUIEvent
  148. * @for Widget
  149. * @param type {String} String representing the name of the event
  150. * @return {String} Event Returns the name of the UI Event, otherwise
  151. * undefined.
  152. */
  153. _getUIEvent: function (type) {
  154.  
  155. if (L.isString(type)) {
  156. var sType = this.parseType(type)[1],
  157. iDelim,
  158. returnVal;
  159.  
  160. if (sType) {
  161. // TODO: Get delimiter from ET, or have ET support this.
  162. iDelim = sType.indexOf(EVENT_PREFIX_DELIMITER);
  163. if (iDelim > -1) {
  164. sType = sType.substring(iDelim + EVENT_PREFIX_DELIMITER.length);
  165. }
  166.  
  167. if (this.UI_EVENTS[sType]) {
  168. returnVal = sType;
  169. }
  170. }
  171.  
  172. return returnVal;
  173. }
  174. },
  175.  
  176. /**
  177. * Sets up infrastructure required to fire a UI event.
  178. *
  179. * @private
  180. * @method _initUIEvent
  181. * @for Widget
  182. * @param type {String} String representing the name of the event
  183. * @return {String}
  184. */
  185. _initUIEvent: function (type) {
  186. var sType = this._getUIEvent(type),
  187. queue = this._uiEvtsInitQueue || {};
  188.  
  189. if (sType && !queue[sType]) {
  190. Y.log("Deferring creation of " + type + " delegate until render.", "info", "widget");
  191.  
  192. this._uiEvtsInitQueue = queue[sType] = 1;
  193.  
  194. this.after(RENDER, function() {
  195. this._createUIEvent(sType);
  196. delete this._uiEvtsInitQueue[sType];
  197. });
  198. }
  199. },
  200.  
  201. // Override of "on" from Base to facilitate the firing of Widget events
  202. // based on DOM events of the same name/type (e.g. "click", "mouseover").
  203. // Temporary solution until we have the ability to listen to when
  204. // someone adds an event listener (bug 2528230)
  205. on: function (type) {
  206. this._initUIEvent(type);
  207. return Widget.superclass.on.apply(this, arguments);
  208. },
  209.  
  210. // Override of "publish" from Base to facilitate the firing of Widget events
  211. // based on DOM events of the same name/type (e.g. "click", "mouseover").
  212. // Temporary solution until we have the ability to listen to when
  213. // someone publishes an event (bug 2528230)
  214. publish: function (type, config) {
  215. var sType = this._getUIEvent(type);
  216. if (sType && config && config.defaultFn) {
  217. this._initUIEvent(sType);
  218. }
  219. return Widget.superclass.publish.apply(this, arguments);
  220. }
  221.  
  222. }, true); // overwrite existing EventTarget methods
  223.