Version 3.18.1
Show:

File: event-custom/js/event-do.js

  1. /**
  2. * Custom event engine, DOM event listener abstraction layer, synthetic DOM
  3. * events.
  4. * @module event-custom
  5. * @submodule event-custom-base
  6. */
  7. /**
  8. * Allows for the insertion of methods that are executed before or after
  9. * a specified method
  10. * @class Do
  11. * @static
  12. */
  13. var DO_BEFORE = 0,
  14. DO_AFTER = 1,
  15. DO = {
  16. /**
  17. * Cache of objects touched by the utility
  18. * @property objs
  19. * @static
  20. * @deprecated Since 3.6.0. The `_yuiaop` property on the AOP'd object
  21. * replaces the role of this property, but is considered to be private, and
  22. * is only mentioned to provide a migration path.
  23. *
  24. * If you have a use case which warrants migration to the _yuiaop property,
  25. * please file a ticket to let us know what it's used for and we can see if
  26. * we need to expose hooks for that functionality more formally.
  27. */
  28. objs: null,
  29. /**
  30. * <p>Execute the supplied method before the specified function. Wrapping
  31. * function may optionally return an instance of the following classes to
  32. * further alter runtime behavior:</p>
  33. * <dl>
  34. * <dt></code>Y.Do.Halt(message, returnValue)</code></dt>
  35. * <dd>Immediatly stop execution and return
  36. * <code>returnValue</code>. No other wrapping functions will be
  37. * executed.</dd>
  38. * <dt></code>Y.Do.AlterArgs(message, newArgArray)</code></dt>
  39. * <dd>Replace the arguments that the original function will be
  40. * called with.</dd>
  41. * <dt></code>Y.Do.Prevent(message)</code></dt>
  42. * <dd>Don't execute the wrapped function. Other before phase
  43. * wrappers will be executed.</dd>
  44. * </dl>
  45. *
  46. * @method before
  47. * @param fn {Function} the function to execute
  48. * @param obj the object hosting the method to displace
  49. * @param sFn {string} the name of the method to displace
  50. * @param c The execution context for fn
  51. * @param arg* {mixed} 0..n additional arguments to supply to the subscriber
  52. * when the event fires.
  53. * @return {EventHandle} handle for the subscription
  54. * @static
  55. */
  56. before: function(fn, obj, sFn, c) {
  57. // Y.log('Do before: ' + sFn, 'info', 'event');
  58. var f = fn, a;
  59. if (c) {
  60. a = [fn, c].concat(Y.Array(arguments, 4, true));
  61. f = Y.rbind.apply(Y, a);
  62. }
  63. return this._inject(DO_BEFORE, f, obj, sFn);
  64. },
  65. /**
  66. * <p>Execute the supplied method after the specified function. Wrapping
  67. * function may optionally return an instance of the following classes to
  68. * further alter runtime behavior:</p>
  69. * <dl>
  70. * <dt></code>Y.Do.Halt(message, returnValue)</code></dt>
  71. * <dd>Immediatly stop execution and return
  72. * <code>returnValue</code>. No other wrapping functions will be
  73. * executed.</dd>
  74. * <dt></code>Y.Do.AlterReturn(message, returnValue)</code></dt>
  75. * <dd>Return <code>returnValue</code> instead of the wrapped
  76. * method's original return value. This can be further altered by
  77. * other after phase wrappers.</dd>
  78. * </dl>
  79. *
  80. * <p>The static properties <code>Y.Do.originalRetVal</code> and
  81. * <code>Y.Do.currentRetVal</code> will be populated for reference.</p>
  82. *
  83. * @method after
  84. * @param fn {Function} the function to execute
  85. * @param obj the object hosting the method to displace
  86. * @param sFn {string} the name of the method to displace
  87. * @param c The execution context for fn
  88. * @param arg* {mixed} 0..n additional arguments to supply to the subscriber
  89. * @return {EventHandle} handle for the subscription
  90. * @static
  91. */
  92. after: function(fn, obj, sFn, c) {
  93. var f = fn, a;
  94. if (c) {
  95. a = [fn, c].concat(Y.Array(arguments, 4, true));
  96. f = Y.rbind.apply(Y, a);
  97. }
  98. return this._inject(DO_AFTER, f, obj, sFn);
  99. },
  100. /**
  101. * Execute the supplied method before or after the specified function.
  102. * Used by <code>before</code> and <code>after</code>.
  103. *
  104. * @method _inject
  105. * @param when {string} before or after
  106. * @param fn {Function} the function to execute
  107. * @param obj the object hosting the method to displace
  108. * @param sFn {string} the name of the method to displace
  109. * @param c The execution context for fn
  110. * @return {EventHandle} handle for the subscription
  111. * @private
  112. * @static
  113. */
  114. _inject: function(when, fn, obj, sFn) {
  115. // object id
  116. var id = Y.stamp(obj), o, sid;
  117. if (!obj._yuiaop) {
  118. // create a map entry for the obj if it doesn't exist, to hold overridden methods
  119. obj._yuiaop = {};
  120. }
  121. o = obj._yuiaop;
  122. if (!o[sFn]) {
  123. // create a map entry for the method if it doesn't exist
  124. o[sFn] = new Y.Do.Method(obj, sFn);
  125. // re-route the method to our wrapper
  126. obj[sFn] = function() {
  127. return o[sFn].exec.apply(o[sFn], arguments);
  128. };
  129. }
  130. // subscriber id
  131. sid = id + Y.stamp(fn) + sFn;
  132. // register the callback
  133. o[sFn].register(sid, fn, when);
  134. return new Y.EventHandle(o[sFn], sid);
  135. },
  136. /**
  137. * Detach a before or after subscription.
  138. *
  139. * @method detach
  140. * @param handle {EventHandle} the subscription handle
  141. * @static
  142. */
  143. detach: function(handle) {
  144. if (handle.detach) {
  145. handle.detach();
  146. }
  147. }
  148. };
  149. Y.Do = DO;
  150. //////////////////////////////////////////////////////////////////////////
  151. /**
  152. * Contains the return value from the wrapped method, accessible
  153. * by 'after' event listeners.
  154. *
  155. * @property originalRetVal
  156. * @static
  157. * @since 3.2.0
  158. */
  159. /**
  160. * Contains the current state of the return value, consumable by
  161. * 'after' event listeners, and updated if an after subscriber
  162. * changes the return value generated by the wrapped function.
  163. *
  164. * @property currentRetVal
  165. * @static
  166. * @since 3.2.0
  167. */
  168. //////////////////////////////////////////////////////////////////////////
  169. /**
  170. * Wrapper for a displaced method with aop enabled
  171. * @class Do.Method
  172. * @constructor
  173. * @param obj The object to operate on
  174. * @param sFn The name of the method to displace
  175. */
  176. DO.Method = function(obj, sFn) {
  177. this.obj = obj;
  178. this.methodName = sFn;
  179. this.method = obj[sFn];
  180. this.before = {};
  181. this.after = {};
  182. };
  183. /**
  184. * Register a aop subscriber
  185. * @method register
  186. * @param sid {string} the subscriber id
  187. * @param fn {Function} the function to execute
  188. * @param when {string} when to execute the function
  189. */
  190. DO.Method.prototype.register = function (sid, fn, when) {
  191. if (when) {
  192. this.after[sid] = fn;
  193. } else {
  194. this.before[sid] = fn;
  195. }
  196. };
  197. /**
  198. * Unregister a aop subscriber
  199. * @method delete
  200. * @param sid {string} the subscriber id
  201. * @param fn {Function} the function to execute
  202. * @param when {string} when to execute the function
  203. */
  204. DO.Method.prototype._delete = function (sid) {
  205. // Y.log('Y.Do._delete: ' + sid, 'info', 'Event');
  206. delete this.before[sid];
  207. delete this.after[sid];
  208. };
  209. /**
  210. * <p>Execute the wrapped method. All arguments are passed into the wrapping
  211. * functions. If any of the before wrappers return an instance of
  212. * <code>Y.Do.Halt</code> or <code>Y.Do.Prevent</code>, neither the wrapped
  213. * function nor any after phase subscribers will be executed.</p>
  214. *
  215. * <p>The return value will be the return value of the wrapped function or one
  216. * provided by a wrapper function via an instance of <code>Y.Do.Halt</code> or
  217. * <code>Y.Do.AlterReturn</code>.
  218. *
  219. * @method exec
  220. * @param arg* {any} Arguments are passed to the wrapping and wrapped functions
  221. * @return {any} Return value of wrapped function unless overwritten (see above)
  222. */
  223. DO.Method.prototype.exec = function () {
  224. var args = Y.Array(arguments, 0, true),
  225. i, ret, newRet,
  226. bf = this.before,
  227. af = this.after,
  228. prevented = false;
  229. // execute before
  230. for (i in bf) {
  231. if (bf.hasOwnProperty(i)) {
  232. ret = bf[i].apply(this.obj, args);
  233. if (ret) {
  234. switch (ret.constructor) {
  235. case DO.Halt:
  236. return ret.retVal;
  237. case DO.AlterArgs:
  238. args = ret.newArgs;
  239. break;
  240. case DO.Prevent:
  241. prevented = true;
  242. break;
  243. default:
  244. }
  245. }
  246. }
  247. }
  248. // execute method
  249. if (!prevented) {
  250. ret = this.method.apply(this.obj, args);
  251. }
  252. DO.originalRetVal = ret;
  253. DO.currentRetVal = ret;
  254. // execute after methods.
  255. for (i in af) {
  256. if (af.hasOwnProperty(i)) {
  257. newRet = af[i].apply(this.obj, args);
  258. // Stop processing if a Halt object is returned
  259. if (newRet && newRet.constructor === DO.Halt) {
  260. return newRet.retVal;
  261. // Check for a new return value
  262. } else if (newRet && newRet.constructor === DO.AlterReturn) {
  263. ret = newRet.newRetVal;
  264. // Update the static retval state
  265. DO.currentRetVal = ret;
  266. }
  267. }
  268. }
  269. return ret;
  270. };
  271. //////////////////////////////////////////////////////////////////////////
  272. /**
  273. * Return an AlterArgs object when you want to change the arguments that
  274. * were passed into the function. Useful for Do.before subscribers. An
  275. * example would be a service that scrubs out illegal characters prior to
  276. * executing the core business logic.
  277. * @class Do.AlterArgs
  278. * @constructor
  279. * @param msg {String} (optional) Explanation of the altered return value
  280. * @param newArgs {Array} Call parameters to be used for the original method
  281. * instead of the arguments originally passed in.
  282. */
  283. DO.AlterArgs = function(msg, newArgs) {
  284. this.msg = msg;
  285. this.newArgs = newArgs;
  286. };
  287. /**
  288. * Return an AlterReturn object when you want to change the result returned
  289. * from the core method to the caller. Useful for Do.after subscribers.
  290. * @class Do.AlterReturn
  291. * @constructor
  292. * @param msg {String} (optional) Explanation of the altered return value
  293. * @param newRetVal {any} Return value passed to code that invoked the wrapped
  294. * function.
  295. */
  296. DO.AlterReturn = function(msg, newRetVal) {
  297. this.msg = msg;
  298. this.newRetVal = newRetVal;
  299. };
  300. /**
  301. * Return a Halt object when you want to terminate the execution
  302. * of all subsequent subscribers as well as the wrapped method
  303. * if it has not exectued yet. Useful for Do.before subscribers.
  304. * @class Do.Halt
  305. * @constructor
  306. * @param msg {String} (optional) Explanation of why the termination was done
  307. * @param retVal {any} Return value passed to code that invoked the wrapped
  308. * function.
  309. */
  310. DO.Halt = function(msg, retVal) {
  311. this.msg = msg;
  312. this.retVal = retVal;
  313. };
  314. /**
  315. * Return a Prevent object when you want to prevent the wrapped function
  316. * from executing, but want the remaining listeners to execute. Useful
  317. * for Do.before subscribers.
  318. * @class Do.Prevent
  319. * @constructor
  320. * @param msg {String} (optional) Explanation of why the termination was done
  321. */
  322. DO.Prevent = function(msg) {
  323. this.msg = msg;
  324. };
  325. /**
  326. * Return an Error object when you want to terminate the execution
  327. * of all subsequent method calls.
  328. * @class Do.Error
  329. * @constructor
  330. * @param msg {String} (optional) Explanation of the altered return value
  331. * @param retVal {any} Return value passed to code that invoked the wrapped
  332. * function.
  333. * @deprecated use Y.Do.Halt or Y.Do.Prevent
  334. */
  335. DO.Error = DO.Halt;
  336. //////////////////////////////////////////////////////////////////////////