emittermixin.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658
  1. /**
  2. * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  4. */
  5. /**
  6. * @module utils/emittermixin
  7. */
  8. import EventInfo from './eventinfo';
  9. import uid from './uid';
  10. import priorities from './priorities';
  11. // To check if component is loaded more than once.
  12. import './version';
  13. import CKEditorError from './ckeditorerror';
  14. const _listeningTo = Symbol( 'listeningTo' );
  15. const _emitterId = Symbol( 'emitterId' );
  16. /**
  17. * Mixin that injects the {@link ~Emitter events API} into its host.
  18. *
  19. * @mixin EmitterMixin
  20. * @implements module:utils/emittermixin~Emitter
  21. */
  22. const EmitterMixin = {
  23. /**
  24. * @inheritDoc
  25. */
  26. on( event, callback, options = {} ) {
  27. this.listenTo( this, event, callback, options );
  28. },
  29. /**
  30. * @inheritDoc
  31. */
  32. once( event, callback, options ) {
  33. let wasFired = false;
  34. const onceCallback = function( event, ...args ) {
  35. // Ensure the callback is called only once even if the callback itself leads to re-firing the event
  36. // (which would call the callback again).
  37. if ( !wasFired ) {
  38. wasFired = true;
  39. // Go off() at the first call.
  40. event.off();
  41. // Go with the original callback.
  42. callback.call( this, event, ...args );
  43. }
  44. };
  45. // Make a similar on() call, simply replacing the callback.
  46. this.listenTo( this, event, onceCallback, options );
  47. },
  48. /**
  49. * @inheritDoc
  50. */
  51. off( event, callback ) {
  52. this.stopListening( this, event, callback );
  53. },
  54. /**
  55. * @inheritDoc
  56. */
  57. listenTo( emitter, event, callback, options = {} ) {
  58. let emitterInfo, eventCallbacks;
  59. // _listeningTo contains a list of emitters that this object is listening to.
  60. // This list has the following format:
  61. //
  62. // _listeningTo: {
  63. // emitterId: {
  64. // emitter: emitter,
  65. // callbacks: {
  66. // event1: [ callback1, callback2, ... ]
  67. // ....
  68. // }
  69. // },
  70. // ...
  71. // }
  72. if ( !this[ _listeningTo ] ) {
  73. this[ _listeningTo ] = {};
  74. }
  75. const emitters = this[ _listeningTo ];
  76. if ( !_getEmitterId( emitter ) ) {
  77. _setEmitterId( emitter );
  78. }
  79. const emitterId = _getEmitterId( emitter );
  80. if ( !( emitterInfo = emitters[ emitterId ] ) ) {
  81. emitterInfo = emitters[ emitterId ] = {
  82. emitter,
  83. callbacks: {}
  84. };
  85. }
  86. if ( !( eventCallbacks = emitterInfo.callbacks[ event ] ) ) {
  87. eventCallbacks = emitterInfo.callbacks[ event ] = [];
  88. }
  89. eventCallbacks.push( callback );
  90. // Finally register the callback to the event.
  91. createEventNamespace( emitter, event );
  92. const lists = getCallbacksListsForNamespace( emitter, event );
  93. const priority = priorities.get( options.priority );
  94. const callbackDefinition = {
  95. callback,
  96. priority
  97. };
  98. // Add the callback to all callbacks list.
  99. for ( const callbacks of lists ) {
  100. // Add the callback to the list in the right priority position.
  101. let added = false;
  102. for ( let i = 0; i < callbacks.length; i++ ) {
  103. if ( callbacks[ i ].priority < priority ) {
  104. callbacks.splice( i, 0, callbackDefinition );
  105. added = true;
  106. break;
  107. }
  108. }
  109. // Add at the end, if right place was not found.
  110. if ( !added ) {
  111. callbacks.push( callbackDefinition );
  112. }
  113. }
  114. },
  115. /**
  116. * @inheritDoc
  117. */
  118. stopListening( emitter, event, callback ) {
  119. const emitters = this[ _listeningTo ];
  120. let emitterId = emitter && _getEmitterId( emitter );
  121. const emitterInfo = emitters && emitterId && emitters[ emitterId ];
  122. const eventCallbacks = emitterInfo && event && emitterInfo.callbacks[ event ];
  123. // Stop if nothing has been listened.
  124. if ( !emitters || ( emitter && !emitterInfo ) || ( event && !eventCallbacks ) ) {
  125. return;
  126. }
  127. // All params provided. off() that single callback.
  128. if ( callback ) {
  129. removeCallback( emitter, event, callback );
  130. }
  131. // Only `emitter` and `event` provided. off() all callbacks for that event.
  132. else if ( eventCallbacks ) {
  133. while ( ( callback = eventCallbacks.pop() ) ) {
  134. removeCallback( emitter, event, callback );
  135. }
  136. delete emitterInfo.callbacks[ event ];
  137. }
  138. // Only `emitter` provided. off() all events for that emitter.
  139. else if ( emitterInfo ) {
  140. for ( event in emitterInfo.callbacks ) {
  141. this.stopListening( emitter, event );
  142. }
  143. delete emitters[ emitterId ];
  144. }
  145. // No params provided. off() all emitters.
  146. else {
  147. for ( emitterId in emitters ) {
  148. this.stopListening( emitters[ emitterId ].emitter );
  149. }
  150. delete this[ _listeningTo ];
  151. }
  152. },
  153. /**
  154. * @inheritDoc
  155. */
  156. fire( eventOrInfo, ...args ) {
  157. try {
  158. const eventInfo = eventOrInfo instanceof EventInfo ? eventOrInfo : new EventInfo( this, eventOrInfo );
  159. const event = eventInfo.name;
  160. let callbacks = getCallbacksForEvent( this, event );
  161. // Record that the event passed this emitter on its path.
  162. eventInfo.path.push( this );
  163. // Handle event listener callbacks first.
  164. if ( callbacks ) {
  165. // Arguments passed to each callback.
  166. const callbackArgs = [ eventInfo, ...args ];
  167. // Copying callbacks array is the easiest and most secure way of preventing infinite loops, when event callbacks
  168. // are added while processing other callbacks. Previous solution involved adding counters (unique ids) but
  169. // failed if callbacks were added to the queue before currently processed callback.
  170. // If this proves to be too inefficient, another method is to change `.on()` so callbacks are stored if same
  171. // event is currently processed. Then, `.fire()` at the end, would have to add all stored events.
  172. callbacks = Array.from( callbacks );
  173. for ( let i = 0; i < callbacks.length; i++ ) {
  174. callbacks[ i ].callback.apply( this, callbackArgs );
  175. // Remove the callback from future requests if off() has been called.
  176. if ( eventInfo.off.called ) {
  177. // Remove the called mark for the next calls.
  178. delete eventInfo.off.called;
  179. removeCallback( this, event, callbacks[ i ].callback );
  180. }
  181. // Do not execute next callbacks if stop() was called.
  182. if ( eventInfo.stop.called ) {
  183. break;
  184. }
  185. }
  186. }
  187. // Delegate event to other emitters if needed.
  188. if ( this._delegations ) {
  189. const destinations = this._delegations.get( event );
  190. const passAllDestinations = this._delegations.get( '*' );
  191. if ( destinations ) {
  192. fireDelegatedEvents( destinations, eventInfo, args );
  193. }
  194. if ( passAllDestinations ) {
  195. fireDelegatedEvents( passAllDestinations, eventInfo, args );
  196. }
  197. }
  198. return eventInfo.return;
  199. } catch ( err ) {
  200. CKEditorError.rethrowUnexpectedError( err, this );
  201. }
  202. },
  203. /**
  204. * @inheritDoc
  205. */
  206. delegate( ...events ) {
  207. return {
  208. to: ( emitter, nameOrFunction ) => {
  209. if ( !this._delegations ) {
  210. this._delegations = new Map();
  211. }
  212. // Originally there was a for..of loop which unfortunately caused an error in Babel that didn't allow
  213. // build an application. See: https://github.com/ckeditor/ckeditor5-react/issues/40.
  214. events.forEach( eventName => {
  215. const destinations = this._delegations.get( eventName );
  216. if ( !destinations ) {
  217. this._delegations.set( eventName, new Map( [ [ emitter, nameOrFunction ] ] ) );
  218. } else {
  219. destinations.set( emitter, nameOrFunction );
  220. }
  221. } );
  222. }
  223. };
  224. },
  225. /**
  226. * @inheritDoc
  227. */
  228. stopDelegating( event, emitter ) {
  229. if ( !this._delegations ) {
  230. return;
  231. }
  232. if ( !event ) {
  233. this._delegations.clear();
  234. } else if ( !emitter ) {
  235. this._delegations.delete( event );
  236. } else {
  237. const destinations = this._delegations.get( event );
  238. if ( destinations ) {
  239. destinations.delete( emitter );
  240. }
  241. }
  242. }
  243. };
  244. export default EmitterMixin;
  245. /**
  246. * Emitter/listener interface.
  247. *
  248. * Can be easily implemented by a class by mixing the {@link module:utils/emittermixin~EmitterMixin} mixin.
  249. *
  250. * @interface Emitter
  251. */
  252. /**
  253. * Registers a callback function to be executed when an event is fired.
  254. *
  255. * Shorthand for {@link #listenTo `this.listenTo( this, event, callback, options )`} (it makes the emitter
  256. * listen on itself).
  257. *
  258. * @method #on
  259. * @param {String} event The name of the event.
  260. * @param {Function} callback The function to be called on event.
  261. * @param {Object} [options={}] Additional options.
  262. * @param {module:utils/priorities~PriorityString|Number} [options.priority='normal'] The priority of this event callback. The higher
  263. * the priority value the sooner the callback will be fired. Events having the same priority are called in the
  264. * order they were added.
  265. */
  266. /**
  267. * Registers a callback function to be executed on the next time the event is fired only. This is similar to
  268. * calling {@link #on} followed by {@link #off} in the callback.
  269. *
  270. * @method #once
  271. * @param {String} event The name of the event.
  272. * @param {Function} callback The function to be called on event.
  273. * @param {Object} [options={}] Additional options.
  274. * @param {module:utils/priorities~PriorityString|Number} [options.priority='normal'] The priority of this event callback. The higher
  275. * the priority value the sooner the callback will be fired. Events having the same priority are called in the
  276. * order they were added.
  277. */
  278. /**
  279. * Stops executing the callback on the given event.
  280. * Shorthand for {@link #stopListening `this.stopListening( this, event, callback )`}.
  281. *
  282. * @method #off
  283. * @param {String} event The name of the event.
  284. * @param {Function} callback The function to stop being called.
  285. */
  286. /**
  287. * Registers a callback function to be executed when an event is fired in a specific (emitter) object.
  288. *
  289. * Events can be grouped in namespaces using `:`.
  290. * When namespaced event is fired, it additionally fires all callbacks for that namespace.
  291. *
  292. * // myEmitter.on( ... ) is a shorthand for myEmitter.listenTo( myEmitter, ... ).
  293. * myEmitter.on( 'myGroup', genericCallback );
  294. * myEmitter.on( 'myGroup:myEvent', specificCallback );
  295. *
  296. * // genericCallback is fired.
  297. * myEmitter.fire( 'myGroup' );
  298. * // both genericCallback and specificCallback are fired.
  299. * myEmitter.fire( 'myGroup:myEvent' );
  300. * // genericCallback is fired even though there are no callbacks for "foo".
  301. * myEmitter.fire( 'myGroup:foo' );
  302. *
  303. * An event callback can {@link module:utils/eventinfo~EventInfo#stop stop the event} and
  304. * set the {@link module:utils/eventinfo~EventInfo#return return value} of the {@link #fire} method.
  305. *
  306. * @method #listenTo
  307. * @param {module:utils/emittermixin~Emitter} emitter The object that fires the event.
  308. * @param {String} event The name of the event.
  309. * @param {Function} callback The function to be called on event.
  310. * @param {Object} [options={}] Additional options.
  311. * @param {module:utils/priorities~PriorityString|Number} [options.priority='normal'] The priority of this event callback. The higher
  312. * the priority value the sooner the callback will be fired. Events having the same priority are called in the
  313. * order they were added.
  314. */
  315. /**
  316. * Stops listening for events. It can be used at different levels:
  317. *
  318. * * To stop listening to a specific callback.
  319. * * To stop listening to a specific event.
  320. * * To stop listening to all events fired by a specific object.
  321. * * To stop listening to all events fired by all objects.
  322. *
  323. * @method #stopListening
  324. * @param {module:utils/emittermixin~Emitter} [emitter] The object to stop listening to. If omitted, stops it for all objects.
  325. * @param {String} [event] (Requires the `emitter`) The name of the event to stop listening to. If omitted, stops it
  326. * for all events from `emitter`.
  327. * @param {Function} [callback] (Requires the `event`) The function to be removed from the call list for the given
  328. * `event`.
  329. */
  330. /**
  331. * Fires an event, executing all callbacks registered for it.
  332. *
  333. * The first parameter passed to callbacks is an {@link module:utils/eventinfo~EventInfo} object,
  334. * followed by the optional `args` provided in the `fire()` method call.
  335. *
  336. * @method #fire
  337. * @param {String|module:utils/eventinfo~EventInfo} eventOrInfo The name of the event or `EventInfo` object if event is delegated.
  338. * @param {...*} [args] Additional arguments to be passed to the callbacks.
  339. * @returns {*} By default the method returns `undefined`. However, the return value can be changed by listeners
  340. * through modification of the {@link module:utils/eventinfo~EventInfo#return `evt.return`}'s property (the event info
  341. * is the first param of every callback).
  342. */
  343. /**
  344. * Delegates selected events to another {@link module:utils/emittermixin~Emitter}. For instance:
  345. *
  346. * emitterA.delegate( 'eventX' ).to( emitterB );
  347. * emitterA.delegate( 'eventX', 'eventY' ).to( emitterC );
  348. *
  349. * then `eventX` is delegated (fired by) `emitterB` and `emitterC` along with `data`:
  350. *
  351. * emitterA.fire( 'eventX', data );
  352. *
  353. * and `eventY` is delegated (fired by) `emitterC` along with `data`:
  354. *
  355. * emitterA.fire( 'eventY', data );
  356. *
  357. * @method #delegate
  358. * @param {...String} events Event names that will be delegated to another emitter.
  359. * @returns {module:utils/emittermixin~EmitterMixinDelegateChain}
  360. */
  361. /**
  362. * Stops delegating events. It can be used at different levels:
  363. *
  364. * * To stop delegating all events.
  365. * * To stop delegating a specific event to all emitters.
  366. * * To stop delegating a specific event to a specific emitter.
  367. *
  368. * @method #stopDelegating
  369. * @param {String} [event] The name of the event to stop delegating. If omitted, stops it all delegations.
  370. * @param {module:utils/emittermixin~Emitter} [emitter] (requires `event`) The object to stop delegating a particular event to.
  371. * If omitted, stops delegation of `event` to all emitters.
  372. */
  373. /**
  374. * Checks if `listeningEmitter` listens to an emitter with given `listenedToEmitterId` and if so, returns that emitter.
  375. * If not, returns `null`.
  376. *
  377. * @protected
  378. * @param {module:utils/emittermixin~Emitter} listeningEmitter An emitter that listens.
  379. * @param {String} listenedToEmitterId Unique emitter id of emitter listened to.
  380. * @returns {module:utils/emittermixin~Emitter|null}
  381. */
  382. export function _getEmitterListenedTo( listeningEmitter, listenedToEmitterId ) {
  383. if ( listeningEmitter[ _listeningTo ] && listeningEmitter[ _listeningTo ][ listenedToEmitterId ] ) {
  384. return listeningEmitter[ _listeningTo ][ listenedToEmitterId ].emitter;
  385. }
  386. return null;
  387. }
  388. /**
  389. * Sets emitter's unique id.
  390. *
  391. * **Note:** `_emitterId` can be set only once.
  392. *
  393. * @protected
  394. * @param {module:utils/emittermixin~Emitter} emitter An emitter for which id will be set.
  395. * @param {String} [id] Unique id to set. If not passed, random unique id will be set.
  396. */
  397. export function _setEmitterId( emitter, id ) {
  398. if ( !emitter[ _emitterId ] ) {
  399. emitter[ _emitterId ] = id || uid();
  400. }
  401. }
  402. /**
  403. * Returns emitter's unique id.
  404. *
  405. * @protected
  406. * @param {module:utils/emittermixin~Emitter} emitter An emitter which id will be returned.
  407. */
  408. export function _getEmitterId( emitter ) {
  409. return emitter[ _emitterId ];
  410. }
  411. // Gets the internal `_events` property of the given object.
  412. // `_events` property store all lists with callbacks for registered event names.
  413. // If there were no events registered on the object, empty `_events` object is created.
  414. function getEvents( source ) {
  415. if ( !source._events ) {
  416. Object.defineProperty( source, '_events', {
  417. value: {}
  418. } );
  419. }
  420. return source._events;
  421. }
  422. // Creates event node for generic-specific events relation architecture.
  423. function makeEventNode() {
  424. return {
  425. callbacks: [],
  426. childEvents: []
  427. };
  428. }
  429. // Creates an architecture for generic-specific events relation.
  430. // If needed, creates all events for given eventName, i.e. if the first registered event
  431. // is foo:bar:abc, it will create foo:bar:abc, foo:bar and foo event and tie them together.
  432. // It also copies callbacks from more generic events to more specific events when
  433. // specific events are created.
  434. function createEventNamespace( source, eventName ) {
  435. const events = getEvents( source );
  436. // First, check if the event we want to add to the structure already exists.
  437. if ( events[ eventName ] ) {
  438. // If it exists, we don't have to do anything.
  439. return;
  440. }
  441. // In other case, we have to create the structure for the event.
  442. // Note, that we might need to create intermediate events too.
  443. // I.e. if foo:bar:abc is being registered and we only have foo in the structure,
  444. // we need to also register foo:bar.
  445. // Currently processed event name.
  446. let name = eventName;
  447. // Name of the event that is a child event for currently processed event.
  448. let childEventName = null;
  449. // Array containing all newly created specific events.
  450. const newEventNodes = [];
  451. // While loop can't check for ':' index because we have to handle generic events too.
  452. // In each loop, we truncate event name, going from the most specific name to the generic one.
  453. // I.e. foo:bar:abc -> foo:bar -> foo.
  454. while ( name !== '' ) {
  455. if ( events[ name ] ) {
  456. // If the currently processed event name is already registered, we can be sure
  457. // that it already has all the structure created, so we can break the loop here
  458. // as no more events need to be registered.
  459. break;
  460. }
  461. // If this event is not yet registered, create a new object for it.
  462. events[ name ] = makeEventNode();
  463. // Add it to the array with newly created events.
  464. newEventNodes.push( events[ name ] );
  465. // Add previously processed event name as a child of this event.
  466. if ( childEventName ) {
  467. events[ name ].childEvents.push( childEventName );
  468. }
  469. childEventName = name;
  470. // If `.lastIndexOf()` returns -1, `.substr()` will return '' which will break the loop.
  471. name = name.substr( 0, name.lastIndexOf( ':' ) );
  472. }
  473. if ( name !== '' ) {
  474. // If name is not empty, we found an already registered event that was a parent of the
  475. // event we wanted to register.
  476. // Copy that event's callbacks to newly registered events.
  477. for ( const node of newEventNodes ) {
  478. node.callbacks = events[ name ].callbacks.slice();
  479. }
  480. // Add last newly created event to the already registered event.
  481. events[ name ].childEvents.push( childEventName );
  482. }
  483. }
  484. // Gets an array containing callbacks list for a given event and it's more specific events.
  485. // I.e. if given event is foo:bar and there is also foo:bar:abc event registered, this will
  486. // return callback list of foo:bar and foo:bar:abc (but not foo).
  487. function getCallbacksListsForNamespace( source, eventName ) {
  488. const eventNode = getEvents( source )[ eventName ];
  489. if ( !eventNode ) {
  490. return [];
  491. }
  492. let callbacksLists = [ eventNode.callbacks ];
  493. for ( let i = 0; i < eventNode.childEvents.length; i++ ) {
  494. const childCallbacksLists = getCallbacksListsForNamespace( source, eventNode.childEvents[ i ] );
  495. callbacksLists = callbacksLists.concat( childCallbacksLists );
  496. }
  497. return callbacksLists;
  498. }
  499. // Get the list of callbacks for a given event, but only if there any callbacks have been registered.
  500. // If there are no callbacks registered for given event, it checks if this is a specific event and looks
  501. // for callbacks for it's more generic version.
  502. function getCallbacksForEvent( source, eventName ) {
  503. let event;
  504. if ( !source._events || !( event = source._events[ eventName ] ) || !event.callbacks.length ) {
  505. // There are no callbacks registered for specified eventName.
  506. // But this could be a specific-type event that is in a namespace.
  507. if ( eventName.indexOf( ':' ) > -1 ) {
  508. // If the eventName is specific, try to find callback lists for more generic event.
  509. return getCallbacksForEvent( source, eventName.substr( 0, eventName.lastIndexOf( ':' ) ) );
  510. } else {
  511. // If this is a top-level generic event, return null;
  512. return null;
  513. }
  514. }
  515. return event.callbacks;
  516. }
  517. // Fires delegated events for given map of destinations.
  518. //
  519. // @private
  520. // * @param {Map.<utils.Emitter>} destinations A map containing
  521. // `[ {@link module:utils/emittermixin~Emitter}, "event name" ]` pair destinations.
  522. // * @param {utils.EventInfo} eventInfo The original event info object.
  523. // * @param {Array.<*>} fireArgs Arguments the original event was fired with.
  524. function fireDelegatedEvents( destinations, eventInfo, fireArgs ) {
  525. for ( let [ emitter, name ] of destinations ) {
  526. if ( !name ) {
  527. name = eventInfo.name;
  528. } else if ( typeof name == 'function' ) {
  529. name = name( eventInfo.name );
  530. }
  531. const delegatedInfo = new EventInfo( eventInfo.source, name );
  532. delegatedInfo.path = [ ...eventInfo.path ];
  533. emitter.fire( delegatedInfo, ...fireArgs );
  534. }
  535. }
  536. // Removes callback from emitter for given event.
  537. //
  538. // @param {module:utils/emittermixin~Emitter} emitter
  539. // @param {String} event
  540. // @param {Function} callback
  541. function removeCallback( emitter, event, callback ) {
  542. const lists = getCallbacksListsForNamespace( emitter, event );
  543. for ( const callbacks of lists ) {
  544. for ( let i = 0; i < callbacks.length; i++ ) {
  545. if ( callbacks[ i ].callback == callback ) {
  546. // Remove the callback from the list (fixing the next index).
  547. callbacks.splice( i, 1 );
  548. i--;
  549. }
  550. }
  551. }
  552. }
  553. /**
  554. * The return value of {@link ~EmitterMixin#delegate}.
  555. *
  556. * @interface module:utils/emittermixin~EmitterMixinDelegateChain
  557. */
  558. /**
  559. * Selects destination for {@link module:utils/emittermixin~EmitterMixin#delegate} events.
  560. *
  561. * @method #to
  562. * @param {module:utils/emittermixin~Emitter} emitter An `EmitterMixin` instance which is the destination for delegated events.
  563. * @param {String|Function} [nameOrFunction] A custom event name or function which converts the original name string.
  564. */