NOBUG: Fixed file access permissions
[moodle.git] / lib / yuilib / 3.13.0 / event-synthetic / event-synthetic.js
blob3c34731b7c58a6f81b314898c554269a99941313
1 /*
2 YUI 3.13.0 (build 508226d)
3 Copyright 2013 Yahoo! Inc. All rights reserved.
4 Licensed under the BSD License.
5 http://yuilibrary.com/license/
6 */
8 YUI.add('event-synthetic', function (Y, NAME) {
10 /**
11  * Define new DOM events that can be subscribed to from Nodes.
12  *
13  * @module event
14  * @submodule event-synthetic
15  */
16 var CustomEvent = Y.CustomEvent,
17     DOMMap   = Y.Env.evt.dom_map,
18     toArray  = Y.Array,
19     YLang    = Y.Lang,
20     isObject = YLang.isObject,
21     isString = YLang.isString,
22     isArray  = YLang.isArray,
23     query    = Y.Selector.query,
24     noop     = function () {};
26 /**
27  * <p>The triggering mechanism used by SyntheticEvents.</p>
28  *
29  * <p>Implementers should not instantiate these directly.  Use the Notifier
30  * provided to the event's implemented <code>on(node, sub, notifier)</code> or
31  * <code>delegate(node, sub, notifier, filter)</code> methods.</p>
32  *
33  * @class SyntheticEvent.Notifier
34  * @constructor
35  * @param handle {EventHandle} the detach handle for the subscription to an
36  *              internal custom event used to execute the callback passed to
37  *              on(..) or delegate(..)
38  * @param emitFacade {Boolean} take steps to ensure the first arg received by
39  *              the subscription callback is an event facade
40  * @private
41  * @since 3.2.0
42  */
43 function Notifier(handle, emitFacade) {
44     this.handle     = handle;
45     this.emitFacade = emitFacade;
48 /**
49  * <p>Executes the subscription callback, passing the firing arguments as the
50  * first parameters to that callback. For events that are configured with
51  * emitFacade=true, it is common practice to pass the triggering DOMEventFacade
52  * as the first parameter.  Barring a proper DOMEventFacade or EventFacade
53  * (from a CustomEvent), a new EventFacade will be generated.  In that case, if
54  * fire() is called with a simple object, it will be mixed into the facade.
55  * Otherwise, the facade will be prepended to the callback parameters.</p>
56  *
57  * <p>For notifiers provided to delegate logic, the first argument should be an
58  * object with a &quot;currentTarget&quot; property to identify what object to
59  * default as 'this' in the callback.  Typically this is gleaned from the
60  * DOMEventFacade or EventFacade, but if configured with emitFacade=false, an
61  * object must be provided.  In that case, the object will be removed from the
62  * callback parameters.</p>
63  *
64  * <p>Additional arguments passed during event subscription will be
65  * automatically added after those passed to fire().</p>
66  *
67  * @method fire
68  * @param e {EventFacade|DOMEventFacade|Object|any} (see description)
69  * @param arg* {any} additional arguments received by all subscriptions
70  * @private
71  */
72 Notifier.prototype.fire = function (e) {
73     // first arg to delegate notifier should be an object with currentTarget
74     var args     = toArray(arguments, 0, true),
75         handle   = this.handle,
76         ce       = handle.evt,
77         sub      = handle.sub,
78         thisObj  = sub.context,
79         delegate = sub.filter,
80         event    = e || {},
81         ret;
83     if (this.emitFacade) {
84         if (!e || !e.preventDefault) {
85             event = ce._getFacade();
87             if (isObject(e) && !e.preventDefault) {
88                 Y.mix(event, e, true);
89                 args[0] = event;
90             } else {
91                 args.unshift(event);
92             }
93         }
95         event.type    = ce.type;
96         event.details = args.slice();
98         if (delegate) {
99             event.container = ce.host;
100         }
101     } else if (delegate && isObject(e) && e.currentTarget) {
102         args.shift();
103     }
105     sub.context = thisObj || event.currentTarget || ce.host;
106     ret = ce.fire.apply(ce, args);
108     // have to handle preventedFn and stoppedFn manually because
109     // Notifier CustomEvents are forced to emitFacade=false
110     if (e.prevented && ce.preventedFn) {
111         ce.preventedFn.apply(ce, args);
112     }
114     if (e.stopped && ce.stoppedFn) {
115         ce.stoppedFn.apply(ce, args);
116     }
118     sub.context = thisObj; // reset for future firing
120     // to capture callbacks that return false to stopPropagation.
121     // Useful for delegate implementations
122     return ret;
126  * Manager object for synthetic event subscriptions to aggregate multiple synths on the
127  * same node without colliding with actual DOM subscription entries in the global map of
128  * DOM subscriptions.  Also facilitates proper cleanup on page unload.
130  * @class SynthRegistry
131  * @constructor
132  * @param el {HTMLElement} the DOM element
133  * @param yuid {String} the yuid stamp for the element
134  * @param key {String} the generated id token used to identify an event type +
135  *                     element in the global DOM subscription map.
136  * @private
137  */
138 function SynthRegistry(el, yuid, key) {
139     this.handles = [];
140     this.el      = el;
141     this.key     = key;
142     this.domkey  = yuid;
145 SynthRegistry.prototype = {
146     constructor: SynthRegistry,
148     // A few object properties to fake the CustomEvent interface for page
149     // unload cleanup.  DON'T TOUCH!
150     type      : '_synth',
151     fn        : noop,
152     capture   : false,
154     /**
155      * Adds a subscription from the Notifier registry.
156      *
157      * @method register
158      * @param handle {EventHandle} the subscription
159      * @since 3.4.0
160      */
161     register: function (handle) {
162         handle.evt.registry = this;
163         this.handles.push(handle);
164     },
166     /**
167      * Removes the subscription from the Notifier registry.
168      *
169      * @method _unregisterSub
170      * @param sub {Subscription} the subscription
171      * @since 3.4.0
172      */
173     unregister: function (sub) {
174         var handles = this.handles,
175             events = DOMMap[this.domkey],
176             i;
178         for (i = handles.length - 1; i >= 0; --i) {
179             if (handles[i].sub === sub) {
180                 handles.splice(i, 1);
181                 break;
182             }
183         }
185         // Clean up left over objects when there are no more subscribers.
186         if (!handles.length) {
187             delete events[this.key];
188             if (!Y.Object.size(events)) {
189                 delete DOMMap[this.domkey];
190             }
191         }
192     },
194     /**
195      * Used by the event system's unload cleanup process.  When navigating
196      * away from the page, the event system iterates the global map of element
197      * subscriptions and detaches everything using detachAll().  Normally,
198      * the map is populated with custom events, so this object needs to
199      * at least support the detachAll method to duck type its way to
200      * cleanliness.
201      *
202      * @method detachAll
203      * @private
204      * @since 3.4.0
205      */
206     detachAll : function () {
207         var handles = this.handles,
208             i = handles.length;
210         while (--i >= 0) {
211             handles[i].detach();
212         }
213     }
217  * <p>Wrapper class for the integration of new events into the YUI event
218  * infrastructure.  Don't instantiate this object directly, use
219  * <code>Y.Event.define(type, config)</code>.  See that method for details.</p>
221  * <p>Properties that MAY or SHOULD be specified in the configuration are noted
222  * below and in the description of <code>Y.Event.define</code>.</p>
224  * @class SyntheticEvent
225  * @constructor
226  * @param cfg {Object} Implementation pieces and configuration
227  * @since 3.1.0
228  * @in event-synthetic
229  */
230 function SyntheticEvent() {
231     this._init.apply(this, arguments);
234 Y.mix(SyntheticEvent, {
235     Notifier: Notifier,
236     SynthRegistry: SynthRegistry,
238     /**
239      * Returns the array of subscription handles for a node for the given event
240      * type.  Passing true as the third argument will create a registry entry
241      * in the event system's DOM map to host the array if one doesn't yet exist.
242      *
243      * @method getRegistry
244      * @param node {Node} the node
245      * @param type {String} the event
246      * @param create {Boolean} create a registration entry to host a new array
247      *                  if one doesn't exist.
248      * @return {Array}
249      * @static
250      * @protected
251      * @since 3.2.0
252      */
253     getRegistry: function (node, type, create) {
254         var el     = node._node,
255             yuid   = Y.stamp(el),
256             key    = 'event:' + yuid + type + '_synth',
257             events = DOMMap[yuid];
259         if (create) {
260             if (!events) {
261                 events = DOMMap[yuid] = {};
262             }
263             if (!events[key]) {
264                 events[key] = new SynthRegistry(el, yuid, key);
265             }
266         }
268         return (events && events[key]) || null;
269     },
271     /**
272      * Alternate <code>_delete()</code> method for the CustomEvent object
273      * created to manage SyntheticEvent subscriptions.
274      *
275      * @method _deleteSub
276      * @param sub {Subscription} the subscription to clean up
277      * @private
278      * @since 3.2.0
279      */
280     _deleteSub: function (sub) {
281         if (sub && sub.fn) {
282             var synth = this.eventDef,
283                 method = (sub.filter) ? 'detachDelegate' : 'detach';
285             this._subscribers = [];
287             if (CustomEvent.keepDeprecatedSubs) {
288                 this.subscribers = {};
289             }
291             synth[method](sub.node, sub, this.notifier, sub.filter);
292             this.registry.unregister(sub);
294             delete sub.fn;
295             delete sub.node;
296             delete sub.context;
297         }
298     },
300     prototype: {
301         constructor: SyntheticEvent,
303         /**
304          * Construction logic for the event.
305          *
306          * @method _init
307          * @protected
308          */
309         _init: function () {
310             var config = this.publishConfig || (this.publishConfig = {});
312             // The notification mechanism handles facade creation
313             this.emitFacade = ('emitFacade' in config) ?
314                                 config.emitFacade :
315                                 true;
316             config.emitFacade  = false;
317         },
319         /**
320          * <p>Implementers MAY provide this method definition.</p>
321          *
322          * <p>Implement this function if the event supports a different
323          * subscription signature.  This function is used by both
324          * <code>on()</code> and <code>delegate()</code>.  The second parameter
325          * indicates that the event is being subscribed via
326          * <code>delegate()</code>.</p>
327          *
328          * <p>Implementations must remove extra arguments from the args list
329          * before returning.  The required args for <code>on()</code>
330          * subscriptions are</p>
331          * <pre><code>[type, callback, target, context, argN...]</code></pre>
332          *
333          * <p>The required args for <code>delegate()</code>
334          * subscriptions are</p>
335          *
336          * <pre><code>[type, callback, target, filter, context, argN...]</code></pre>
337          *
338          * <p>The return value from this function will be stored on the
339          * subscription in the '_extra' property for reference elsewhere.</p>
340          *
341          * @method processArgs
342          * @param args {Array} parmeters passed to Y.on(..) or Y.delegate(..)
343          * @param delegate {Boolean} true if the subscription is from Y.delegate
344          * @return {any}
345          */
346         processArgs: noop,
348         /**
349          * <p>Implementers MAY override this property.</p>
350          *
351          * <p>Whether to prevent multiple subscriptions to this event that are
352          * classified as being the same.  By default, this means the subscribed
353          * callback is the same function.  See the <code>subMatch</code>
354          * method.  Setting this to true will impact performance for high volume
355          * events.</p>
356          *
357          * @property preventDups
358          * @type {Boolean}
359          * @default false
360          */
361         //preventDups  : false,
363         /**
364          * <p>Implementers SHOULD provide this method definition.</p>
365          *
366          * Implementation logic for subscriptions done via <code>node.on(type,
367          * fn)</code> or <code>Y.on(type, fn, target)</code>.  This
368          * function should set up the monitor(s) that will eventually fire the
369          * event.  Typically this involves subscribing to at least one DOM
370          * event.  It is recommended to store detach handles from any DOM
371          * subscriptions to make for easy cleanup in the <code>detach</code>
372          * method.  Typically these handles are added to the <code>sub</code>
373          * object.  Also for SyntheticEvents that leverage a single DOM
374          * subscription under the hood, it is recommended to pass the DOM event
375          * object to <code>notifier.fire(e)</code>.  (The event name on the
376          * object will be updated).
377          *
378          * @method on
379          * @param node {Node} the node the subscription is being applied to
380          * @param sub {Subscription} the object to track this subscription
381          * @param notifier {SyntheticEvent.Notifier} call notifier.fire(..) to
382          *              trigger the execution of the subscribers
383          */
384         on: noop,
386         /**
387          * <p>Implementers SHOULD provide this method definition.</p>
388          *
389          * <p>Implementation logic for detaching subscriptions done via
390          * <code>node.on(type, fn)</code>.  This function should clean up any
391          * subscriptions made in the <code>on()</code> phase.</p>
392          *
393          * @method detach
394          * @param node {Node} the node the subscription was applied to
395          * @param sub {Subscription} the object tracking this subscription
396          * @param notifier {SyntheticEvent.Notifier} the Notifier used to
397          *              trigger the execution of the subscribers
398          */
399         detach: noop,
401         /**
402          * <p>Implementers SHOULD provide this method definition.</p>
403          *
404          * <p>Implementation logic for subscriptions done via
405          * <code>node.delegate(type, fn, filter)</code> or
406          * <code>Y.delegate(type, fn, container, filter)</code>.  Like with
407          * <code>on()</code> above, this function should monitor the environment
408          * for the event being fired, and trigger subscription execution by
409          * calling <code>notifier.fire(e)</code>.</p>
410          *
411          * <p>This function receives a fourth argument, which is the filter
412          * used to identify which Node's are of interest to the subscription.
413          * The filter will be either a boolean function that accepts a target
414          * Node for each hierarchy level as the event bubbles, or a selector
415          * string.  To translate selector strings into filter functions, use
416          * <code>Y.delegate.compileFilter(filter)</code>.</p>
417          *
418          * @method delegate
419          * @param node {Node} the node the subscription is being applied to
420          * @param sub {Subscription} the object to track this subscription
421          * @param notifier {SyntheticEvent.Notifier} call notifier.fire(..) to
422          *              trigger the execution of the subscribers
423          * @param filter {String|Function} Selector string or function that
424          *              accepts an event object and returns null, a Node, or an
425          *              array of Nodes matching the criteria for processing.
426          * @since 3.2.0
427          */
428         delegate       : noop,
430         /**
431          * <p>Implementers SHOULD provide this method definition.</p>
432          *
433          * <p>Implementation logic for detaching subscriptions done via
434          * <code>node.delegate(type, fn, filter)</code> or
435          * <code>Y.delegate(type, fn, container, filter)</code>.  This function
436          * should clean up any subscriptions made in the
437          * <code>delegate()</code> phase.</p>
438          *
439          * @method detachDelegate
440          * @param node {Node} the node the subscription was applied to
441          * @param sub {Subscription} the object tracking this subscription
442          * @param notifier {SyntheticEvent.Notifier} the Notifier used to
443          *              trigger the execution of the subscribers
444          * @param filter {String|Function} Selector string or function that
445          *              accepts an event object and returns null, a Node, or an
446          *              array of Nodes matching the criteria for processing.
447          * @since 3.2.0
448          */
449         detachDelegate : noop,
451         /**
452          * Sets up the boilerplate for detaching the event and facilitating the
453          * execution of subscriber callbacks.
454          *
455          * @method _on
456          * @param args {Array} array of arguments passed to
457          *              <code>Y.on(...)</code> or <code>Y.delegate(...)</code>
458          * @param delegate {Boolean} true if called from
459          * <code>Y.delegate(...)</code>
460          * @return {EventHandle} the detach handle for this subscription
461          * @private
462          * since 3.2.0
463          */
464         _on: function (args, delegate) {
465             var handles  = [],
466                 originalArgs = args.slice(),
467                 extra    = this.processArgs(args, delegate),
468                 selector = args[2],
469                 method   = delegate ? 'delegate' : 'on',
470                 nodes, handle;
472             // Can't just use Y.all because it doesn't support window (yet?)
473             nodes = (isString(selector)) ?
474                 query(selector) :
475                 toArray(selector || Y.one(Y.config.win));
477             if (!nodes.length && isString(selector)) {
478                 handle = Y.on('available', function () {
479                     Y.mix(handle, Y[method].apply(Y, originalArgs), true);
480                 }, selector);
482                 return handle;
483             }
485             Y.Array.each(nodes, function (node) {
486                 var subArgs = args.slice(),
487                     filter;
489                 node = Y.one(node);
491                 if (node) {
492                     if (delegate) {
493                         filter = subArgs.splice(3, 1)[0];
494                     }
496                     // (type, fn, el, thisObj, ...) => (fn, thisObj, ...)
497                     subArgs.splice(0, 4, subArgs[1], subArgs[3]);
499                     if (!this.preventDups ||
500                         !this.getSubs(node, args, null, true))
501                     {
502                         handles.push(this._subscribe(node, method, subArgs, extra, filter));
503                     }
504                 }
505             }, this);
507             return (handles.length === 1) ?
508                 handles[0] :
509                 new Y.EventHandle(handles);
510         },
512         /**
513          * Creates a new Notifier object for use by this event's
514          * <code>on(...)</code> or <code>delegate(...)</code> implementation
515          * and register the custom event proxy in the DOM system for cleanup.
516          *
517          * @method _subscribe
518          * @param node {Node} the Node hosting the event
519          * @param method {String} "on" or "delegate"
520          * @param args {Array} the subscription arguments passed to either
521          *              <code>Y.on(...)</code> or <code>Y.delegate(...)</code>
522          *              after running through <code>processArgs(args)</code> to
523          *              normalize the argument signature
524          * @param extra {any} Extra data parsed from
525          *              <code>processArgs(args)</code>
526          * @param filter {String|Function} the selector string or function
527          *              filter passed to <code>Y.delegate(...)</code> (not
528          *              present when called from <code>Y.on(...)</code>)
529          * @return {EventHandle}
530          * @private
531          * @since 3.2.0
532          */
533         _subscribe: function (node, method, args, extra, filter) {
534             var dispatcher = new Y.CustomEvent(this.type, this.publishConfig),
535                 handle     = dispatcher.on.apply(dispatcher, args),
536                 notifier   = new Notifier(handle, this.emitFacade),
537                 registry   = SyntheticEvent.getRegistry(node, this.type, true),
538                 sub        = handle.sub;
540             sub.node   = node;
541             sub.filter = filter;
542             if (extra) {
543                 this.applyArgExtras(extra, sub);
544             }
546             Y.mix(dispatcher, {
547                 eventDef     : this,
548                 notifier     : notifier,
549                 host         : node,       // I forget what this is for
550                 currentTarget: node,       // for generating facades
551                 target       : node,       // for generating facades
552                 el           : node._node, // For category detach
554                 _delete      : SyntheticEvent._deleteSub
555             }, true);
557             handle.notifier = notifier;
559             registry.register(handle);
561             // Call the implementation's "on" or "delegate" method
562             this[method](node, sub, notifier, filter);
564             return handle;
565         },
567         /**
568          * <p>Implementers MAY provide this method definition.</p>
569          *
570          * <p>Implement this function if you want extra data extracted during
571          * processArgs to be propagated to subscriptions on a per-node basis.
572          * That is to say, if you call <code>Y.on('xyz', fn, xtra, 'div')</code>
573          * the data returned from processArgs will be shared
574          * across the subscription objects for all the divs.  If you want each
575          * subscription to receive unique information, do that processing
576          * here.</p>
577          *
578          * <p>The default implementation adds the data extracted by processArgs
579          * to the subscription object as <code>sub._extra</code>.</p>
580          *
581          * @method applyArgExtras
582          * @param extra {any} Any extra data extracted from processArgs
583          * @param sub {Subscription} the individual subscription
584          */
585         applyArgExtras: function (extra, sub) {
586             sub._extra = extra;
587         },
589         /**
590          * Removes the subscription(s) from the internal subscription dispatch
591          * mechanism.  See <code>SyntheticEvent._deleteSub</code>.
592          *
593          * @method _detach
594          * @param args {Array} The arguments passed to
595          *                  <code>node.detach(...)</code>
596          * @private
597          * @since 3.2.0
598          */
599         _detach: function (args) {
600             // Can't use Y.all because it doesn't support window (yet?)
601             // TODO: Does Y.all support window now?
602             var target = args[2],
603                 els    = (isString(target)) ?
604                             query(target) : toArray(target),
605                 node, i, len, handles, j;
607             // (type, fn, el, context, filter?) => (type, fn, context, filter?)
608             args.splice(2, 1);
610             for (i = 0, len = els.length; i < len; ++i) {
611                 node = Y.one(els[i]);
613                 if (node) {
614                     handles = this.getSubs(node, args);
616                     if (handles) {
617                         for (j = handles.length - 1; j >= 0; --j) {
618                             handles[j].detach();
619                         }
620                     }
621                 }
622             }
623         },
625         /**
626          * Returns the detach handles of subscriptions on a node that satisfy a
627          * search/filter function.  By default, the filter used is the
628          * <code>subMatch</code> method.
629          *
630          * @method getSubs
631          * @param node {Node} the node hosting the event
632          * @param args {Array} the array of original subscription args passed
633          *              to <code>Y.on(...)</code> (before
634          *              <code>processArgs</code>
635          * @param filter {Function} function used to identify a subscription
636          *              for inclusion in the returned array
637          * @param first {Boolean} stop after the first match (used to check for
638          *              duplicate subscriptions)
639          * @return {EventHandle[]} detach handles for the matching subscriptions
640          */
641         getSubs: function (node, args, filter, first) {
642             var registry = SyntheticEvent.getRegistry(node, this.type),
643                 handles  = [],
644                 allHandles, i, len, handle;
646             if (registry) {
647                 allHandles = registry.handles;
649                 if (!filter) {
650                     filter = this.subMatch;
651                 }
653                 for (i = 0, len = allHandles.length; i < len; ++i) {
654                     handle = allHandles[i];
655                     if (filter.call(this, handle.sub, args)) {
656                         if (first) {
657                             return handle;
658                         } else {
659                             handles.push(allHandles[i]);
660                         }
661                     }
662                 }
663             }
665             return handles.length && handles;
666         },
668         /**
669          * <p>Implementers MAY override this to define what constitutes a
670          * &quot;same&quot; subscription.  Override implementations should
671          * consider the lack of a comparator as a match, so calling
672          * <code>getSubs()</code> with no arguments will return all subs.</p>
673          *
674          * <p>Compares a set of subscription arguments against a Subscription
675          * object to determine if they match.  The default implementation
676          * compares the callback function against the second argument passed to
677          * <code>Y.on(...)</code> or <code>node.detach(...)</code> etc.</p>
678          *
679          * @method subMatch
680          * @param sub {Subscription} the existing subscription
681          * @param args {Array} the calling arguments passed to
682          *                  <code>Y.on(...)</code> etc.
683          * @return {Boolean} true if the sub can be described by the args
684          *                  present
685          * @since 3.2.0
686          */
687         subMatch: function (sub, args) {
688             // Default detach cares only about the callback matching
689             return !args[1] || sub.fn === args[1];
690         }
691     }
692 }, true);
694 Y.SyntheticEvent = SyntheticEvent;
697  * <p>Defines a new event in the DOM event system.  Implementers are
698  * responsible for monitoring for a scenario whereby the event is fired.  A
699  * notifier object is provided to the functions identified below.  When the
700  * criteria defining the event are met, call notifier.fire( [args] ); to
701  * execute event subscribers.</p>
703  * <p>The first parameter is the name of the event.  The second parameter is a
704  * configuration object which define the behavior of the event system when the
705  * new event is subscribed to or detached from.  The methods that should be
706  * defined in this configuration object are <code>on</code>,
707  * <code>detach</code>, <code>delegate</code>, and <code>detachDelegate</code>.
708  * You are free to define any other methods or properties needed to define your
709  * event.  Be aware, however, that since the object is used to subclass
710  * SyntheticEvent, you should avoid method names used by SyntheticEvent unless
711  * your intention is to override the default behavior.</p>
713  * <p>This is a list of properties and methods that you can or should specify
714  * in the configuration object:</p>
716  * <dl>
717  *   <dt><code>on</code></dt>
718  *       <dd><code>function (node, subscription, notifier)</code> The
719  *       implementation logic for subscription.  Any special setup you need to
720  *       do to create the environment for the event being fired--E.g. native
721  *       DOM event subscriptions.  Store subscription related objects and
722  *       state on the <code>subscription</code> object.  When the
723  *       criteria have been met to fire the synthetic event, call
724  *       <code>notifier.fire(e)</code>.  See Notifier's <code>fire()</code>
725  *       method for details about what to pass as parameters.</dd>
727  *   <dt><code>detach</code></dt>
728  *       <dd><code>function (node, subscription, notifier)</code> The
729  *       implementation logic for cleaning up a detached subscription. E.g.
730  *       detach any DOM subscriptions added in <code>on</code>.</dd>
732  *   <dt><code>delegate</code></dt>
733  *       <dd><code>function (node, subscription, notifier, filter)</code> The
734  *       implementation logic for subscription via <code>Y.delegate</code> or
735  *       <code>node.delegate</code>.  The filter is typically either a selector
736  *       string or a function.  You can use
737  *       <code>Y.delegate.compileFilter(selectorString)</code> to create a
738  *       filter function from a selector string if needed.  The filter function
739  *       expects an event object as input and should output either null, a
740  *       matching Node, or an array of matching Nodes.  Otherwise, this acts
741  *       like <code>on</code> DOM event subscriptions.  Store subscription
742  *       related objects and information on the <code>subscription</code>
743  *       object.  When the criteria have been met to fire the synthetic event,
744  *       call <code>notifier.fire(e)</code> as noted above.</dd>
746  *   <dt><code>detachDelegate</code></dt>
747  *       <dd><code>function (node, subscription, notifier)</code> The
748  *       implementation logic for cleaning up a detached delegate subscription.
749  *       E.g. detach any DOM delegate subscriptions added in
750  *       <code>delegate</code>.</dd>
752  *   <dt><code>publishConfig</code></dt>
753  *       <dd>(Object) The configuration object that will be used to instantiate
754  *       the underlying CustomEvent. See Notifier's <code>fire</code> method
755  *       for details.</dd>
757  *   <dt><code>processArgs</code></dt
758  *       <dd>
759  *          <p><code>function (argArray, fromDelegate)</code> Optional method
760  *          to extract any additional arguments from the subscription
761  *          signature.  Using this allows <code>on</code> or
762  *          <code>delegate</code> signatures like
763  *          <code>node.on(&quot;hover&quot;, overCallback,
764  *          outCallback)</code>.</p>
765  *          <p>When processing an atypical argument signature, make sure the
766  *          args array is returned to the normal signature before returning
767  *          from the function.  For example, in the &quot;hover&quot; example
768  *          above, the <code>outCallback</code> needs to be <code>splice</code>d
769  *          out of the array.  The expected signature of the args array for
770  *          <code>on()</code> subscriptions is:</p>
771  *          <pre>
772  *              <code>[type, callback, target, contextOverride, argN...]</code>
773  *          </pre>
774  *          <p>And for <code>delegate()</code>:</p>
775  *          <pre>
776  *              <code>[type, callback, target, filter, contextOverride, argN...]</code>
777  *          </pre>
778  *          <p>where <code>target</code> is the node the event is being
779  *          subscribed for.  You can see these signatures documented for
780  *          <code>Y.on()</code> and <code>Y.delegate()</code> respectively.</p>
781  *          <p>Whatever gets returned from the function will be stored on the
782  *          <code>subscription</code> object under
783  *          <code>subscription._extra</code>.</p></dd>
784  *   <dt><code>subMatch</code></dt>
785  *       <dd>
786  *           <p><code>function (sub, args)</code>  Compares a set of
787  *           subscription arguments against a Subscription object to determine
788  *           if they match.  The default implementation compares the callback
789  *           function against the second argument passed to
790  *           <code>Y.on(...)</code> or <code>node.detach(...)</code> etc.</p>
791  *       </dd>
792  * </dl>
794  * @method define
795  * @param type {String} the name of the event
796  * @param config {Object} the prototype definition for the new event (see above)
797  * @param force {Boolean} override an existing event (use with caution)
798  * @return {SyntheticEvent} the subclass implementation instance created to
799  *              handle event subscriptions of this type
800  * @static
801  * @for Event
802  * @since 3.1.0
803  * @in event-synthetic
804  */
805 Y.Event.define = function (type, config, force) {
806     var eventDef, Impl, synth;
808     if (type && type.type) {
809         eventDef = type;
810         force = config;
811     } else if (config) {
812         eventDef = Y.merge({ type: type }, config);
813     }
815     if (eventDef) {
816         if (force || !Y.Node.DOM_EVENTS[eventDef.type]) {
817             Impl = function () {
818                 SyntheticEvent.apply(this, arguments);
819             };
820             Y.extend(Impl, SyntheticEvent, eventDef);
821             synth = new Impl();
823             type = synth.type;
825             Y.Node.DOM_EVENTS[type] = Y.Env.evt.plugins[type] = {
826                 eventDef: synth,
828                 on: function () {
829                     return synth._on(toArray(arguments));
830                 },
832                 delegate: function () {
833                     return synth._on(toArray(arguments), true);
834                 },
836                 detach: function () {
837                     return synth._detach(toArray(arguments));
838                 }
839             };
841         }
842     } else if (isString(type) || isArray(type)) {
843         Y.Array.each(toArray(type), function (t) {
844             Y.Node.DOM_EVENTS[t] = 1;
845         });
846     }
848     return synth;
852 }, '3.13.0', {"requires": ["node-base", "event-custom-complex"]});