API Docs for: 3.17.2
Show:

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

  1.  
  2. /**
  3. * Custom event engine, DOM event listener abstraction layer, synthetic DOM
  4. * events.
  5. * @module event-custom
  6. * @submodule event-custom-base
  7. */
  8.  
  9. /**
  10. * EventTarget provides the implementation for any object to
  11. * publish, subscribe and fire to custom events, and also
  12. * alows other EventTargets to target the object with events
  13. * sourced from the other object.
  14. * EventTarget is designed to be used with Y.augment to wrap
  15. * EventCustom in an interface that allows events to be listened to
  16. * and fired by name. This makes it possible for implementing code to
  17. * subscribe to an event that either has not been created yet, or will
  18. * not be created at all.
  19. * @class EventTarget
  20. * @param opts a configuration object
  21. * @config emitFacade {boolean} if true, all events will emit event
  22. * facade payloads by default (default false)
  23. * @config prefix {String} the prefix to apply to non-prefixed event names
  24. */
  25.  
  26. var L = Y.Lang,
  27. PREFIX_DELIMITER = ':',
  28. CATEGORY_DELIMITER = '|',
  29. AFTER_PREFIX = '~AFTER~',
  30. WILD_TYPE_RE = /(.*?)(:)(.*?)/,
  31.  
  32. _wildType = Y.cached(function(type) {
  33. return type.replace(WILD_TYPE_RE, "*$2$3");
  34. }),
  35.  
  36. /**
  37. * If the instance has a prefix attribute and the
  38. * event type is not prefixed, the instance prefix is
  39. * applied to the supplied type.
  40. * @method _getType
  41. * @private
  42. */
  43. _getType = function(type, pre) {
  44.  
  45. if (!pre || !type || type.indexOf(PREFIX_DELIMITER) > -1) {
  46. return type;
  47. }
  48.  
  49. return pre + PREFIX_DELIMITER + type;
  50. },
  51.  
  52. /**
  53. * Returns an array with the detach key (if provided),
  54. * and the prefixed event name from _getType
  55. * Y.on('detachcategory| menu:click', fn)
  56. * @method _parseType
  57. * @private
  58. */
  59. _parseType = Y.cached(function(type, pre) {
  60.  
  61. var t = type, detachcategory, after, i;
  62.  
  63. if (!L.isString(t)) {
  64. return t;
  65. }
  66.  
  67. i = t.indexOf(AFTER_PREFIX);
  68.  
  69. if (i > -1) {
  70. after = true;
  71. t = t.substr(AFTER_PREFIX.length);
  72. }
  73.  
  74. i = t.indexOf(CATEGORY_DELIMITER);
  75.  
  76. if (i > -1) {
  77. detachcategory = t.substr(0, (i));
  78. t = t.substr(i+1);
  79. if (t === '*') {
  80. t = null;
  81. }
  82. }
  83.  
  84. // detach category, full type with instance prefix, is this an after listener, short type
  85. return [detachcategory, (pre) ? _getType(t, pre) : t, after, t];
  86. }),
  87.  
  88. ET = function(opts) {
  89.  
  90. var etState = this._yuievt,
  91. etConfig;
  92.  
  93. if (!etState) {
  94. etState = this._yuievt = {
  95. events: {}, // PERF: Not much point instantiating lazily. We're bound to have events
  96. targets: null, // PERF: Instantiate lazily, if user actually adds target,
  97. config: {
  98. host: this,
  99. context: this
  100. },
  101. chain: Y.config.chain
  102. };
  103. }
  104.  
  105. etConfig = etState.config;
  106.  
  107. if (opts) {
  108. mixConfigs(etConfig, opts, true);
  109.  
  110. if (opts.chain !== undefined) {
  111. etState.chain = opts.chain;
  112. }
  113.  
  114. if (opts.prefix) {
  115. etConfig.prefix = opts.prefix;
  116. }
  117. }
  118. };
  119.  
  120. ET.prototype = {
  121.  
  122. constructor: ET,
  123.  
  124. /**
  125. * Listen to a custom event hosted by this object one time.
  126. * This is the equivalent to <code>on</code> except the
  127. * listener is immediatelly detached when it is executed.
  128. * @method once
  129. * @param {String} type The name of the event
  130. * @param {Function} fn The callback to execute in response to the event
  131. * @param {Object} [context] Override `this` object in callback
  132. * @param {Any} [arg*] 0..n additional arguments to supply to the subscriber
  133. * @return {EventHandle} A subscription handle capable of detaching the
  134. * subscription
  135. */
  136. once: function() {
  137. var handle = this.on.apply(this, arguments);
  138. handle.batch(function(hand) {
  139. if (hand.sub) {
  140. hand.sub.once = true;
  141. }
  142. });
  143. return handle;
  144. },
  145.  
  146. /**
  147. * Listen to a custom event hosted by this object one time.
  148. * This is the equivalent to <code>after</code> except the
  149. * listener is immediatelly detached when it is executed.
  150. * @method onceAfter
  151. * @param {String} type The name of the event
  152. * @param {Function} fn The callback to execute in response to the event
  153. * @param {Object} [context] Override `this` object in callback
  154. * @param {Any} [arg*] 0..n additional arguments to supply to the subscriber
  155. * @return {EventHandle} A subscription handle capable of detaching that
  156. * subscription
  157. */
  158. onceAfter: function() {
  159. var handle = this.after.apply(this, arguments);
  160. handle.batch(function(hand) {
  161. if (hand.sub) {
  162. hand.sub.once = true;
  163. }
  164. });
  165. return handle;
  166. },
  167.  
  168. /**
  169. * Takes the type parameter passed to 'on' and parses out the
  170. * various pieces that could be included in the type. If the
  171. * event type is passed without a prefix, it will be expanded
  172. * to include the prefix one is supplied or the event target
  173. * is configured with a default prefix.
  174. * @method parseType
  175. * @param {String} type the type
  176. * @param {String} [pre] The prefix. Defaults to this._yuievt.config.prefix
  177. * @since 3.3.0
  178. * @return {Array} an array containing:
  179. * * the detach category, if supplied,
  180. * * the prefixed event type,
  181. * * whether or not this is an after listener,
  182. * * the supplied event type
  183. */
  184. parseType: function(type, pre) {
  185. return _parseType(type, pre || this._yuievt.config.prefix);
  186. },
  187.  
  188. /**
  189. * Subscribe a callback function to a custom event fired by this object or
  190. * from an object that bubbles its events to this object.
  191. *
  192. * Callback functions for events published with `emitFacade = true` will
  193. * receive an `EventFacade` as the first argument (typically named "e").
  194. * These callbacks can then call `e.preventDefault()` to disable the
  195. * behavior published to that event's `defaultFn`. See the `EventFacade`
  196. * API for all available properties and methods. Subscribers to
  197. * non-`emitFacade` events will receive the arguments passed to `fire()`
  198. * after the event name.
  199. *
  200. * To subscribe to multiple events at once, pass an object as the first
  201. * argument, where the key:value pairs correspond to the eventName:callback,
  202. * or pass an array of event names as the first argument to subscribe to
  203. * all listed events with the same callback.
  204. *
  205. * Returning `false` from a callback is supported as an alternative to
  206. * calling `e.preventDefault(); e.stopPropagation();`. However, it is
  207. * recommended to use the event methods whenever possible.
  208. *
  209. * @method on
  210. * @param {String} type The name of the event
  211. * @param {Function} fn The callback to execute in response to the event
  212. * @param {Object} [context] Override `this` object in callback
  213. * @param {Any} [arg*] 0..n additional arguments to supply to the subscriber
  214. * @return {EventHandle} A subscription handle capable of detaching that
  215. * subscription
  216. */
  217. on: function(type, fn, context) {
  218.  
  219. var yuievt = this._yuievt,
  220. parts = _parseType(type, yuievt.config.prefix), f, c, args, ret, ce,
  221. detachcategory, handle, store = Y.Env.evt.handles, after, adapt, shorttype,
  222. Node = Y.Node, n, domevent, isArr;
  223.  
  224. // full name, args, detachcategory, after
  225. this._monitor('attach', parts[1], {
  226. args: arguments,
  227. category: parts[0],
  228. after: parts[2]
  229. });
  230.  
  231. if (L.isObject(type)) {
  232.  
  233. if (L.isFunction(type)) {
  234. return Y.Do.before.apply(Y.Do, arguments);
  235. }
  236.  
  237. f = fn;
  238. c = context;
  239. args = nativeSlice.call(arguments, 0);
  240. ret = [];
  241.  
  242. if (L.isArray(type)) {
  243. isArr = true;
  244. }
  245.  
  246. after = type._after;
  247. delete type._after;
  248.  
  249. Y.each(type, function(v, k) {
  250.  
  251. if (L.isObject(v)) {
  252. f = v.fn || ((L.isFunction(v)) ? v : f);
  253. c = v.context || c;
  254. }
  255.  
  256. var nv = (after) ? AFTER_PREFIX : '';
  257.  
  258. args[0] = nv + ((isArr) ? v : k);
  259. args[1] = f;
  260. args[2] = c;
  261.  
  262. ret.push(this.on.apply(this, args));
  263.  
  264. }, this);
  265.  
  266. return (yuievt.chain) ? this : new Y.EventHandle(ret);
  267. }
  268.  
  269. detachcategory = parts[0];
  270. after = parts[2];
  271. shorttype = parts[3];
  272.  
  273. // extra redirection so we catch adaptor events too. take a look at this.
  274. if (Node && Y.instanceOf(this, Node) && (shorttype in Node.DOM_EVENTS)) {
  275. args = nativeSlice.call(arguments, 0);
  276. args.splice(2, 0, Node.getDOMNode(this));
  277. // Y.log("Node detected, redirecting with these args: " + args);
  278. return Y.on.apply(Y, args);
  279. }
  280.  
  281. type = parts[1];
  282.  
  283. if (Y.instanceOf(this, YUI)) {
  284.  
  285. adapt = Y.Env.evt.plugins[type];
  286. args = nativeSlice.call(arguments, 0);
  287. args[0] = shorttype;
  288.  
  289. if (Node) {
  290. n = args[2];
  291.  
  292. if (Y.instanceOf(n, Y.NodeList)) {
  293. n = Y.NodeList.getDOMNodes(n);
  294. } else if (Y.instanceOf(n, Node)) {
  295. n = Node.getDOMNode(n);
  296. }
  297.  
  298. domevent = (shorttype in Node.DOM_EVENTS);
  299.  
  300. // Captures both DOM events and event plugins.
  301. if (domevent) {
  302. args[2] = n;
  303. }
  304. }
  305.  
  306. // check for the existance of an event adaptor
  307. if (adapt) {
  308. Y.log('Using adaptor for ' + shorttype + ', ' + n, 'info', 'event');
  309. handle = adapt.on.apply(Y, args);
  310. } else if ((!type) || domevent) {
  311. handle = Y.Event._attach(args);
  312. }
  313.  
  314. }
  315.  
  316. if (!handle) {
  317. ce = yuievt.events[type] || this.publish(type);
  318. handle = ce._on(fn, context, (arguments.length > 3) ? nativeSlice.call(arguments, 3) : null, (after) ? 'after' : true);
  319.  
  320. // TODO: More robust regex, accounting for category
  321. if (type.indexOf("*:") !== -1) {
  322. this._hasSiblings = true;
  323. }
  324. }
  325.  
  326. if (detachcategory) {
  327. store[detachcategory] = store[detachcategory] || {};
  328. store[detachcategory][type] = store[detachcategory][type] || [];
  329. store[detachcategory][type].push(handle);
  330. }
  331.  
  332. return (yuievt.chain) ? this : handle;
  333.  
  334. },
  335.  
  336. /**
  337. * subscribe to an event
  338. * @method subscribe
  339. * @deprecated use on
  340. */
  341. subscribe: function() {
  342. Y.log('EventTarget subscribe() is deprecated, use on()', 'warn', 'deprecated');
  343. return this.on.apply(this, arguments);
  344. },
  345.  
  346. /**
  347. * Detach one or more listeners the from the specified event
  348. * @method detach
  349. * @param type {string|Object} Either the handle to the subscriber or the
  350. * type of event. If the type
  351. * is not specified, it will attempt to remove
  352. * the listener from all hosted events.
  353. * @param fn {Function} The subscribed function to unsubscribe, if not
  354. * supplied, all subscribers will be removed.
  355. * @param context {Object} The custom object passed to subscribe. This is
  356. * optional, but if supplied will be used to
  357. * disambiguate multiple listeners that are the same
  358. * (e.g., you subscribe many object using a function
  359. * that lives on the prototype)
  360. * @return {EventTarget} the host
  361. */
  362. detach: function(type, fn, context) {
  363.  
  364. var evts = this._yuievt.events,
  365. i,
  366. Node = Y.Node,
  367. isNode = Node && (Y.instanceOf(this, Node));
  368.  
  369. // detachAll disabled on the Y instance.
  370. if (!type && (this !== Y)) {
  371. for (i in evts) {
  372. if (evts.hasOwnProperty(i)) {
  373. evts[i].detach(fn, context);
  374. }
  375. }
  376. if (isNode) {
  377. Y.Event.purgeElement(Node.getDOMNode(this));
  378. }
  379.  
  380. return this;
  381. }
  382.  
  383. var parts = _parseType(type, this._yuievt.config.prefix),
  384. detachcategory = L.isArray(parts) ? parts[0] : null,
  385. shorttype = (parts) ? parts[3] : null,
  386. adapt, store = Y.Env.evt.handles, detachhost, cat, args,
  387. ce,
  388.  
  389. keyDetacher = function(lcat, ltype, host) {
  390. var handles = lcat[ltype], ce, i;
  391. if (handles) {
  392. for (i = handles.length - 1; i >= 0; --i) {
  393. ce = handles[i].evt;
  394. if (ce.host === host || ce.el === host) {
  395. handles[i].detach();
  396. }
  397. }
  398. }
  399. };
  400.  
  401. if (detachcategory) {
  402.  
  403. cat = store[detachcategory];
  404. type = parts[1];
  405. detachhost = (isNode) ? Y.Node.getDOMNode(this) : this;
  406.  
  407. if (cat) {
  408. if (type) {
  409. keyDetacher(cat, type, detachhost);
  410. } else {
  411. for (i in cat) {
  412. if (cat.hasOwnProperty(i)) {
  413. keyDetacher(cat, i, detachhost);
  414. }
  415. }
  416. }
  417.  
  418. return this;
  419. }
  420.  
  421. // If this is an event handle, use it to detach
  422. } else if (L.isObject(type) && type.detach) {
  423. type.detach();
  424. return this;
  425. // extra redirection so we catch adaptor events too. take a look at this.
  426. } else if (isNode && ((!shorttype) || (shorttype in Node.DOM_EVENTS))) {
  427. args = nativeSlice.call(arguments, 0);
  428. args[2] = Node.getDOMNode(this);
  429. Y.detach.apply(Y, args);
  430. return this;
  431. }
  432.  
  433. adapt = Y.Env.evt.plugins[shorttype];
  434.  
  435. // The YUI instance handles DOM events and adaptors
  436. if (Y.instanceOf(this, YUI)) {
  437. args = nativeSlice.call(arguments, 0);
  438. // use the adaptor specific detach code if
  439. if (adapt && adapt.detach) {
  440. adapt.detach.apply(Y, args);
  441. return this;
  442. // DOM event fork
  443. } else if (!type || (!adapt && Node && (type in Node.DOM_EVENTS))) {
  444. args[0] = type;
  445. Y.Event.detach.apply(Y.Event, args);
  446. return this;
  447. }
  448. }
  449.  
  450. // ce = evts[type];
  451. ce = evts[parts[1]];
  452. if (ce) {
  453. ce.detach(fn, context);
  454. }
  455.  
  456. return this;
  457. },
  458.  
  459. /**
  460. * detach a listener
  461. * @method unsubscribe
  462. * @deprecated use detach
  463. */
  464. unsubscribe: function() {
  465. Y.log('EventTarget unsubscribe() is deprecated, use detach()', 'warn', 'deprecated');
  466. return this.detach.apply(this, arguments);
  467. },
  468.  
  469. /**
  470. * Removes all listeners from the specified event. If the event type
  471. * is not specified, all listeners from all hosted custom events will
  472. * be removed.
  473. * @method detachAll
  474. * @param type {String} The type, or name of the event
  475. */
  476. detachAll: function(type) {
  477. return this.detach(type);
  478. },
  479.  
  480. /**
  481. * Removes all listeners from the specified event. If the event type
  482. * is not specified, all listeners from all hosted custom events will
  483. * be removed.
  484. * @method unsubscribeAll
  485. * @param type {String} The type, or name of the event
  486. * @deprecated use detachAll
  487. */
  488. unsubscribeAll: function() {
  489. Y.log('EventTarget unsubscribeAll() is deprecated, use detachAll()', 'warn', 'deprecated');
  490. return this.detachAll.apply(this, arguments);
  491. },
  492.  
  493. /**
  494. * Creates a new custom event of the specified type. If a custom event
  495. * by that name already exists, it will not be re-created. In either
  496. * case the custom event is returned.
  497. *
  498. * @method publish
  499. *
  500. * @param type {String} the type, or name of the event
  501. * @param opts {object} optional config params. Valid properties are:
  502. *
  503. * <ul>
  504. * <li>
  505. * 'broadcast': whether or not the YUI instance and YUI global are notified when the event is fired (false)
  506. * </li>
  507. * <li>
  508. * 'bubbles': whether or not this event bubbles (true)
  509. * Events can only bubble if emitFacade is true.
  510. * </li>
  511. * <li>
  512. * 'context': the default execution context for the listeners (this)
  513. * </li>
  514. * <li>
  515. * 'defaultFn': the default function to execute when this event fires if preventDefault was not called
  516. * </li>
  517. * <li>
  518. * 'emitFacade': whether or not this event emits a facade (false)
  519. * </li>
  520. * <li>
  521. * 'prefix': the prefix for this targets events, e.g., 'menu' in 'menu:click'
  522. * </li>
  523. * <li>
  524. * 'fireOnce': if an event is configured to fire once, new subscribers after
  525. * the fire will be notified immediately.
  526. * </li>
  527. * <li>
  528. * 'async': fireOnce event listeners will fire synchronously if the event has already
  529. * fired unless async is true.
  530. * </li>
  531. * <li>
  532. * 'preventable': whether or not preventDefault() has an effect (true)
  533. * </li>
  534. * <li>
  535. * 'preventedFn': a function that is executed when preventDefault is called
  536. * </li>
  537. * <li>
  538. * 'queuable': whether or not this event can be queued during bubbling (false)
  539. * </li>
  540. * <li>
  541. * 'silent': if silent is true, debug messages are not provided for this event.
  542. * </li>
  543. * <li>
  544. * 'stoppedFn': a function that is executed when stopPropagation is called
  545. * </li>
  546. *
  547. * <li>
  548. * 'monitored': specifies whether or not this event should send notifications about
  549. * when the event has been attached, detached, or published.
  550. * </li>
  551. * <li>
  552. * 'type': the event type (valid option if not provided as the first parameter to publish)
  553. * </li>
  554. * </ul>
  555. *
  556. * @return {CustomEvent} the custom event
  557. *
  558. */
  559. publish: function(type, opts) {
  560.  
  561. var ret,
  562. etState = this._yuievt,
  563. etConfig = etState.config,
  564. pre = etConfig.prefix;
  565.  
  566. if (typeof type === "string") {
  567. if (pre) {
  568. type = _getType(type, pre);
  569. }
  570. ret = this._publish(type, etConfig, opts);
  571. } else {
  572. ret = {};
  573.  
  574. Y.each(type, function(v, k) {
  575. if (pre) {
  576. k = _getType(k, pre);
  577. }
  578. ret[k] = this._publish(k, etConfig, v || opts);
  579. }, this);
  580.  
  581. }
  582.  
  583. return ret;
  584. },
  585.  
  586. /**
  587. * Returns the fully qualified type, given a short type string.
  588. * That is, returns "foo:bar" when given "bar" if "foo" is the configured prefix.
  589. *
  590. * NOTE: This method, unlike _getType, does no checking of the value passed in, and
  591. * is designed to be used with the low level _publish() method, for critical path
  592. * implementations which need to fast-track publish for performance reasons.
  593. *
  594. * @method _getFullType
  595. * @private
  596. * @param {String} type The short type to prefix
  597. * @return {String} The prefixed type, if a prefix is set, otherwise the type passed in
  598. */
  599. _getFullType : function(type) {
  600.  
  601. var pre = this._yuievt.config.prefix;
  602.  
  603. if (pre) {
  604. return pre + PREFIX_DELIMITER + type;
  605. } else {
  606. return type;
  607. }
  608. },
  609.  
  610. /**
  611. * The low level event publish implementation. It expects all the massaging to have been done
  612. * outside of this method. e.g. the `type` to `fullType` conversion. It's designed to be a fast
  613. * path publish, which can be used by critical code paths to improve performance.
  614. *
  615. * @method _publish
  616. * @private
  617. * @param {String} fullType The prefixed type of the event to publish.
  618. * @param {Object} etOpts The EventTarget specific configuration to mix into the published event.
  619. * @param {Object} ceOpts The publish specific configuration to mix into the published event.
  620. * @return {CustomEvent} The published event. If called without `etOpts` or `ceOpts`, this will
  621. * be the default `CustomEvent` instance, and can be configured independently.
  622. */
  623. _publish : function(fullType, etOpts, ceOpts) {
  624.  
  625. var ce,
  626. etState = this._yuievt,
  627. etConfig = etState.config,
  628. host = etConfig.host,
  629. context = etConfig.context,
  630. events = etState.events;
  631.  
  632. ce = events[fullType];
  633.  
  634. // PERF: Hate to pull the check out of monitor, but trying to keep critical path tight.
  635. if ((etConfig.monitored && !ce) || (ce && ce.monitored)) {
  636. this._monitor('publish', fullType, {
  637. args: arguments
  638. });
  639. }
  640.  
  641. if (!ce) {
  642. // Publish event
  643. ce = events[fullType] = new Y.CustomEvent(fullType, etOpts);
  644.  
  645. if (!etOpts) {
  646. ce.host = host;
  647. ce.context = context;
  648. }
  649. }
  650.  
  651. if (ceOpts) {
  652. mixConfigs(ce, ceOpts, true);
  653. }
  654.  
  655. return ce;
  656. },
  657.  
  658. /**
  659. * This is the entry point for the event monitoring system.
  660. * You can monitor 'attach', 'detach', 'fire', and 'publish'.
  661. * When configured, these events generate an event. click ->
  662. * click_attach, click_detach, click_publish -- these can
  663. * be subscribed to like other events to monitor the event
  664. * system. Inividual published events can have monitoring
  665. * turned on or off (publish can't be turned off before it
  666. * it published) by setting the events 'monitor' config.
  667. *
  668. * @method _monitor
  669. * @param what {String} 'attach', 'detach', 'fire', or 'publish'
  670. * @param eventType {String|CustomEvent} The prefixed name of the event being monitored, or the CustomEvent object.
  671. * @param o {Object} Information about the event interaction, such as
  672. * fire() args, subscription category, publish config
  673. * @private
  674. */
  675. _monitor: function(what, eventType, o) {
  676. var monitorevt, ce, type;
  677.  
  678. if (eventType) {
  679. if (typeof eventType === "string") {
  680. type = eventType;
  681. ce = this.getEvent(eventType, true);
  682. } else {
  683. ce = eventType;
  684. type = eventType.type;
  685. }
  686.  
  687. if ((this._yuievt.config.monitored && (!ce || ce.monitored)) || (ce && ce.monitored)) {
  688. monitorevt = type + '_' + what;
  689. o.monitored = what;
  690. this.fire.call(this, monitorevt, o);
  691. }
  692. }
  693. },
  694.  
  695. /**
  696. * Fire a custom event by name. The callback functions will be executed
  697. * from the context specified when the event was created, and with the
  698. * following parameters.
  699. *
  700. * The first argument is the event type, and any additional arguments are
  701. * passed to the listeners as parameters. If the first of these is an
  702. * object literal, and the event is configured to emit an event facade,
  703. * that object is mixed into the event facade and the facade is provided
  704. * in place of the original object.
  705. *
  706. * If the custom event object hasn't been created, then the event hasn't
  707. * been published and it has no subscribers. For performance sake, we
  708. * immediate exit in this case. This means the event won't bubble, so
  709. * if the intention is that a bubble target be notified, the event must
  710. * be published on this object first.
  711. *
  712. * @method fire
  713. * @param type {String|Object} The type of the event, or an object that contains
  714. * a 'type' property.
  715. * @param arguments {Object*} an arbitrary set of parameters to pass to
  716. * the handler. If the first of these is an object literal and the event is
  717. * configured to emit an event facade, the event facade will replace that
  718. * parameter after the properties the object literal contains are copied to
  719. * the event facade.
  720. * @return {Boolean} True if the whole lifecycle of the event went through,
  721. * false if at any point the event propagation was halted.
  722. */
  723. fire: function(type) {
  724.  
  725. var typeIncluded = (typeof type === "string"),
  726. argCount = arguments.length,
  727. t = type,
  728. yuievt = this._yuievt,
  729. etConfig = yuievt.config,
  730. pre = etConfig.prefix,
  731. ret,
  732. ce,
  733. ce2,
  734. args;
  735.  
  736. if (typeIncluded && argCount <= 3) {
  737.  
  738. // PERF: Try to avoid slice/iteration for the common signatures
  739.  
  740. // Most common
  741. if (argCount === 2) {
  742. args = [arguments[1]]; // fire("foo", {})
  743. } else if (argCount === 3) {
  744. args = [arguments[1], arguments[2]]; // fire("foo", {}, opts)
  745. } else {
  746. args = []; // fire("foo")
  747. }
  748.  
  749. } else {
  750. args = nativeSlice.call(arguments, ((typeIncluded) ? 1 : 0));
  751. }
  752.  
  753. if (!typeIncluded) {
  754. t = (type && type.type);
  755. }
  756.  
  757. if (pre) {
  758. t = _getType(t, pre);
  759. }
  760.  
  761. ce = yuievt.events[t];
  762.  
  763. if (this._hasSiblings) {
  764. ce2 = this.getSibling(t, ce);
  765.  
  766. if (ce2 && !ce) {
  767. ce = this.publish(t);
  768. }
  769. }
  770.  
  771. // PERF: trying to avoid function call, since this is a critical path
  772. if ((etConfig.monitored && (!ce || ce.monitored)) || (ce && ce.monitored)) {
  773. this._monitor('fire', (ce || t), {
  774. args: args
  775. });
  776. }
  777.  
  778. // this event has not been published or subscribed to
  779. if (!ce) {
  780. if (yuievt.hasTargets) {
  781. return this.bubble({ type: t }, args, this);
  782. }
  783.  
  784. // otherwise there is nothing to be done
  785. ret = true;
  786. } else {
  787.  
  788. if (ce2) {
  789. ce.sibling = ce2;
  790. }
  791.  
  792. ret = ce._fire(args);
  793. }
  794.  
  795. return (yuievt.chain) ? this : ret;
  796. },
  797.  
  798. getSibling: function(type, ce) {
  799. var ce2;
  800.  
  801. // delegate to *:type events if there are subscribers
  802. if (type.indexOf(PREFIX_DELIMITER) > -1) {
  803. type = _wildType(type);
  804. ce2 = this.getEvent(type, true);
  805. if (ce2) {
  806. ce2.applyConfig(ce);
  807. ce2.bubbles = false;
  808. ce2.broadcast = 0;
  809. }
  810. }
  811.  
  812. return ce2;
  813. },
  814.  
  815. /**
  816. * Returns the custom event of the provided type has been created, a
  817. * falsy value otherwise
  818. * @method getEvent
  819. * @param type {String} the type, or name of the event
  820. * @param prefixed {String} if true, the type is prefixed already
  821. * @return {CustomEvent} the custom event or null
  822. */
  823. getEvent: function(type, prefixed) {
  824. var pre, e;
  825.  
  826. if (!prefixed) {
  827. pre = this._yuievt.config.prefix;
  828. type = (pre) ? _getType(type, pre) : type;
  829. }
  830. e = this._yuievt.events;
  831. return e[type] || null;
  832. },
  833.  
  834. /**
  835. * Subscribe to a custom event hosted by this object. The
  836. * supplied callback will execute after any listeners add
  837. * via the subscribe method, and after the default function,
  838. * if configured for the event, has executed.
  839. *
  840. * @method after
  841. * @param {String} type The name of the event
  842. * @param {Function} fn The callback to execute in response to the event
  843. * @param {Object} [context] Override `this` object in callback
  844. * @param {Any} [arg*] 0..n additional arguments to supply to the subscriber
  845. * @return {EventHandle} A subscription handle capable of detaching the
  846. * subscription
  847. */
  848. after: function(type, fn) {
  849.  
  850. var a = nativeSlice.call(arguments, 0);
  851.  
  852. switch (L.type(type)) {
  853. case 'function':
  854. return Y.Do.after.apply(Y.Do, arguments);
  855. case 'array':
  856. // YArray.each(a[0], function(v) {
  857. // v = AFTER_PREFIX + v;
  858. // });
  859. // break;
  860. case 'object':
  861. a[0]._after = true;
  862. break;
  863. default:
  864. a[0] = AFTER_PREFIX + type;
  865. }
  866.  
  867. return this.on.apply(this, a);
  868.  
  869. },
  870.  
  871. /**
  872. * Executes the callback before a DOM event, custom event
  873. * or method. If the first argument is a function, it
  874. * is assumed the target is a method. For DOM and custom
  875. * events, this is an alias for Y.on.
  876. *
  877. * For DOM and custom events:
  878. * type, callback, context, 0-n arguments
  879. *
  880. * For methods:
  881. * callback, object (method host), methodName, context, 0-n arguments
  882. *
  883. * @method before
  884. * @return detach handle
  885. */
  886. before: function() {
  887. return this.on.apply(this, arguments);
  888. }
  889.  
  890. };
  891.  
  892. Y.EventTarget = ET;
  893.  
  894. // make Y an event target
  895. Y.mix(Y, ET.prototype);
  896. ET.call(Y, { bubbles: false });
  897.  
  898. YUI.Env.globalEvents = YUI.Env.globalEvents || new ET();
  899.  
  900. /**
  901. * Hosts YUI page level events. This is where events bubble to
  902. * when the broadcast config is set to 2. This property is
  903. * only available if the custom event module is loaded.
  904. * @property Global
  905. * @type EventTarget
  906. * @for YUI
  907. */
  908. Y.Global = YUI.Env.globalEvents;
  909.  
  910. // @TODO implement a global namespace function on Y.Global?
  911.  
  912. /**
  913. `Y.on()` can do many things:
  914.  
  915. <ul>
  916. <li>Subscribe to custom events `publish`ed and `fire`d from Y</li>
  917. <li>Subscribe to custom events `publish`ed with `broadcast` 1 or 2 and
  918. `fire`d from any object in the YUI instance sandbox</li>
  919. <li>Subscribe to DOM events</li>
  920. <li>Subscribe to the execution of a method on any object, effectively
  921. treating that method as an event</li>
  922. </ul>
  923.  
  924. For custom event subscriptions, pass the custom event name as the first argument
  925. and callback as the second. The `this` object in the callback will be `Y` unless
  926. an override is passed as the third argument.
  927.  
  928. Y.on('io:complete', function () {
  929. Y.MyApp.updateStatus('Transaction complete');
  930. });
  931.  
  932. To subscribe to DOM events, pass the name of a DOM event as the first argument
  933. and a CSS selector string as the third argument after the callback function.
  934. Alternately, the third argument can be a `Node`, `NodeList`, `HTMLElement`,
  935. array, or simply omitted (the default is the `window` object).
  936.  
  937. Y.on('click', function (e) {
  938. e.preventDefault();
  939.  
  940. // proceed with ajax form submission
  941. var url = this.get('action');
  942. ...
  943. }, '#my-form');
  944.  
  945. The `this` object in DOM event callbacks will be the `Node` targeted by the CSS
  946. selector or other identifier.
  947.  
  948. `on()` subscribers for DOM events or custom events `publish`ed with a
  949. `defaultFn` can prevent the default behavior with `e.preventDefault()` from the
  950. event object passed as the first parameter to the subscription callback.
  951.  
  952. To subscribe to the execution of an object method, pass arguments corresponding to the call signature for
  953. <a href="../classes/Do.html#methods_before">`Y.Do.before(...)`</a>.
  954.  
  955. NOTE: The formal parameter list below is for events, not for function
  956. injection. See `Y.Do.before` for that signature.
  957.  
  958. @method on
  959. @param {String} type DOM or custom event name
  960. @param {Function} fn The callback to execute in response to the event
  961. @param {Object} [context] Override `this` object in callback
  962. @param {Any} [arg*] 0..n additional arguments to supply to the subscriber
  963. @return {EventHandle} A subscription handle capable of detaching the
  964. subscription
  965. @see Do.before
  966. @for YUI
  967. **/
  968.  
  969. /**
  970. Listen for an event one time. Equivalent to `on()`, except that
  971. the listener is immediately detached when executed.
  972.  
  973. See the <a href="#methods_on">`on()` method</a> for additional subscription
  974. options.
  975.  
  976. @see on
  977. @method once
  978. @param {String} type DOM or custom event name
  979. @param {Function} fn The callback to execute in response to the event
  980. @param {Object} [context] Override `this` object in callback
  981. @param {Any} [arg*] 0..n additional arguments to supply to the subscriber
  982. @return {EventHandle} A subscription handle capable of detaching the
  983. subscription
  984. @for YUI
  985. **/
  986.  
  987. /**
  988. Listen for an event one time. Equivalent to `once()`, except, like `after()`,
  989. the subscription callback executes after all `on()` subscribers and the event's
  990. `defaultFn` (if configured) have executed. Like `after()` if any `on()` phase
  991. subscriber calls `e.preventDefault()`, neither the `defaultFn` nor the `after()`
  992. subscribers will execute.
  993.  
  994. The listener is immediately detached when executed.
  995.  
  996. See the <a href="#methods_on">`on()` method</a> for additional subscription
  997. options.
  998.  
  999. @see once
  1000. @method onceAfter
  1001. @param {String} type The custom event name
  1002. @param {Function} fn The callback to execute in response to the event
  1003. @param {Object} [context] Override `this` object in callback
  1004. @param {Any} [arg*] 0..n additional arguments to supply to the subscriber
  1005. @return {EventHandle} A subscription handle capable of detaching the
  1006. subscription
  1007. @for YUI
  1008. **/
  1009.  
  1010. /**
  1011. Like `on()`, this method creates a subscription to a custom event or to the
  1012. execution of a method on an object.
  1013.  
  1014. For events, `after()` subscribers are executed after the event's
  1015. `defaultFn` unless `e.preventDefault()` was called from an `on()` subscriber.
  1016.  
  1017. See the <a href="#methods_on">`on()` method</a> for additional subscription
  1018. options.
  1019.  
  1020. NOTE: The subscription signature shown is for events, not for function
  1021. injection. See <a href="../classes/Do.html#methods_after">`Y.Do.after`</a>
  1022. for that signature.
  1023.  
  1024. @see on
  1025. @see Do.after
  1026. @method after
  1027. @param {String} type The custom event name
  1028. @param {Function} fn The callback to execute in response to the event
  1029. @param {Object} [context] Override `this` object in callback
  1030. @param {Any} [args*] 0..n additional arguments to supply to the subscriber
  1031. @return {EventHandle} A subscription handle capable of detaching the
  1032. subscription
  1033. @for YUI
  1034. **/
  1035.