Event: Don't break focus triggering after `.on(focus).off(focus)`
[jquery.git] / src / event.js
blob4b6eb00e4fde107fcba89a1883c95bd81f3fffd8
1 import jQuery from "./core.js";
2 import document from "./var/document.js";
3 import documentElement from "./var/documentElement.js";
4 import rnothtmlwhite from "./var/rnothtmlwhite.js";
5 import rcheckableType from "./var/rcheckableType.js";
6 import slice from "./var/slice.js";
7 import acceptData from "./data/var/acceptData.js";
8 import dataPriv from "./data/var/dataPriv.js";
9 import nodeName from "./core/nodeName.js";
11 import "./core/init.js";
12 import "./selector.js";
14 var rtypenamespace = /^([^.]*)(?:\.(.+)|)/;
16 function returnTrue() {
17         return true;
20 function returnFalse() {
21         return false;
24 // Support: IE <=9 - 11+
25 // focus() and blur() are asynchronous, except when they are no-op.
26 // So expect focus to be synchronous when the element is already active,
27 // and blur to be synchronous when the element is not already active.
28 // (focus and blur are always synchronous in other supported browsers,
29 // this just defines when we can count on it).
30 function expectSync( elem, type ) {
31         return ( elem === document.activeElement ) === ( type === "focus" );
34 function on( elem, types, selector, data, fn, one ) {
35         var origFn, type;
37         // Types can be a map of types/handlers
38         if ( typeof types === "object" ) {
40                 // ( types-Object, selector, data )
41                 if ( typeof selector !== "string" ) {
43                         // ( types-Object, data )
44                         data = data || selector;
45                         selector = undefined;
46                 }
47                 for ( type in types ) {
48                         on( elem, type, selector, data, types[ type ], one );
49                 }
50                 return elem;
51         }
53         if ( data == null && fn == null ) {
55                 // ( types, fn )
56                 fn = selector;
57                 data = selector = undefined;
58         } else if ( fn == null ) {
59                 if ( typeof selector === "string" ) {
61                         // ( types, selector, fn )
62                         fn = data;
63                         data = undefined;
64                 } else {
66                         // ( types, data, fn )
67                         fn = data;
68                         data = selector;
69                         selector = undefined;
70                 }
71         }
72         if ( fn === false ) {
73                 fn = returnFalse;
74         } else if ( !fn ) {
75                 return elem;
76         }
78         if ( one === 1 ) {
79                 origFn = fn;
80                 fn = function( event ) {
82                         // Can use an empty set, since event contains the info
83                         jQuery().off( event );
84                         return origFn.apply( this, arguments );
85                 };
87                 // Use same guid so caller can remove using origFn
88                 fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
89         }
90         return elem.each( function() {
91                 jQuery.event.add( this, types, fn, data, selector );
92         } );
96  * Helper functions for managing events -- not part of the public interface.
97  * Props to Dean Edwards' addEvent library for many of the ideas.
98  */
99 jQuery.event = {
101         add: function( elem, types, handler, data, selector ) {
103                 var handleObjIn, eventHandle, tmp,
104                         events, t, handleObj,
105                         special, handlers, type, namespaces, origType,
106                         elemData = dataPriv.get( elem );
108                 // Only attach events to objects that accept data
109                 if ( !acceptData( elem ) ) {
110                         return;
111                 }
113                 // Caller can pass in an object of custom data in lieu of the handler
114                 if ( handler.handler ) {
115                         handleObjIn = handler;
116                         handler = handleObjIn.handler;
117                         selector = handleObjIn.selector;
118                 }
120                 // Ensure that invalid selectors throw exceptions at attach time
121                 // Evaluate against documentElement in case elem is a non-element node (e.g., document)
122                 if ( selector ) {
123                         jQuery.find.matchesSelector( documentElement, selector );
124                 }
126                 // Make sure that the handler has a unique ID, used to find/remove it later
127                 if ( !handler.guid ) {
128                         handler.guid = jQuery.guid++;
129                 }
131                 // Init the element's event structure and main handler, if this is the first
132                 if ( !( events = elemData.events ) ) {
133                         events = elemData.events = Object.create( null );
134                 }
135                 if ( !( eventHandle = elemData.handle ) ) {
136                         eventHandle = elemData.handle = function( e ) {
138                                 // Discard the second event of a jQuery.event.trigger() and
139                                 // when an event is called after a page has unloaded
140                                 return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ?
141                                         jQuery.event.dispatch.apply( elem, arguments ) : undefined;
142                         };
143                 }
145                 // Handle multiple events separated by a space
146                 types = ( types || "" ).match( rnothtmlwhite ) || [ "" ];
147                 t = types.length;
148                 while ( t-- ) {
149                         tmp = rtypenamespace.exec( types[ t ] ) || [];
150                         type = origType = tmp[ 1 ];
151                         namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort();
153                         // There *must* be a type, no attaching namespace-only handlers
154                         if ( !type ) {
155                                 continue;
156                         }
158                         // If event changes its type, use the special event handlers for the changed type
159                         special = jQuery.event.special[ type ] || {};
161                         // If selector defined, determine special event api type, otherwise given type
162                         type = ( selector ? special.delegateType : special.bindType ) || type;
164                         // Update special based on newly reset type
165                         special = jQuery.event.special[ type ] || {};
167                         // handleObj is passed to all event handlers
168                         handleObj = jQuery.extend( {
169                                 type: type,
170                                 origType: origType,
171                                 data: data,
172                                 handler: handler,
173                                 guid: handler.guid,
174                                 selector: selector,
175                                 needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
176                                 namespace: namespaces.join( "." )
177                         }, handleObjIn );
179                         // Init the event handler queue if we're the first
180                         if ( !( handlers = events[ type ] ) ) {
181                                 handlers = events[ type ] = [];
182                                 handlers.delegateCount = 0;
184                                 // Only use addEventListener if the special events handler returns false
185                                 if ( !special.setup ||
186                                         special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
188                                         if ( elem.addEventListener ) {
189                                                 elem.addEventListener( type, eventHandle );
190                                         }
191                                 }
192                         }
194                         if ( special.add ) {
195                                 special.add.call( elem, handleObj );
197                                 if ( !handleObj.handler.guid ) {
198                                         handleObj.handler.guid = handler.guid;
199                                 }
200                         }
202                         // Add to the element's handler list, delegates in front
203                         if ( selector ) {
204                                 handlers.splice( handlers.delegateCount++, 0, handleObj );
205                         } else {
206                                 handlers.push( handleObj );
207                         }
208                 }
210         },
212         // Detach an event or set of events from an element
213         remove: function( elem, types, handler, selector, mappedTypes ) {
215                 var j, origCount, tmp,
216                         events, t, handleObj,
217                         special, handlers, type, namespaces, origType,
218                         elemData = dataPriv.hasData( elem ) && dataPriv.get( elem );
220                 if ( !elemData || !( events = elemData.events ) ) {
221                         return;
222                 }
224                 // Once for each type.namespace in types; type may be omitted
225                 types = ( types || "" ).match( rnothtmlwhite ) || [ "" ];
226                 t = types.length;
227                 while ( t-- ) {
228                         tmp = rtypenamespace.exec( types[ t ] ) || [];
229                         type = origType = tmp[ 1 ];
230                         namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort();
232                         // Unbind all events (on this namespace, if provided) for the element
233                         if ( !type ) {
234                                 for ( type in events ) {
235                                         jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
236                                 }
237                                 continue;
238                         }
240                         special = jQuery.event.special[ type ] || {};
241                         type = ( selector ? special.delegateType : special.bindType ) || type;
242                         handlers = events[ type ] || [];
243                         tmp = tmp[ 2 ] &&
244                                 new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" );
246                         // Remove matching events
247                         origCount = j = handlers.length;
248                         while ( j-- ) {
249                                 handleObj = handlers[ j ];
251                                 if ( ( mappedTypes || origType === handleObj.origType ) &&
252                                         ( !handler || handler.guid === handleObj.guid ) &&
253                                         ( !tmp || tmp.test( handleObj.namespace ) ) &&
254                                         ( !selector || selector === handleObj.selector ||
255                                                 selector === "**" && handleObj.selector ) ) {
256                                         handlers.splice( j, 1 );
258                                         if ( handleObj.selector ) {
259                                                 handlers.delegateCount--;
260                                         }
261                                         if ( special.remove ) {
262                                                 special.remove.call( elem, handleObj );
263                                         }
264                                 }
265                         }
267                         // Remove generic event handler if we removed something and no more handlers exist
268                         // (avoids potential for endless recursion during removal of special event handlers)
269                         if ( origCount && !handlers.length ) {
270                                 if ( !special.teardown ||
271                                         special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
273                                         jQuery.removeEvent( elem, type, elemData.handle );
274                                 }
276                                 delete events[ type ];
277                         }
278                 }
280                 // Remove data and the expando if it's no longer used
281                 if ( jQuery.isEmptyObject( events ) ) {
282                         dataPriv.remove( elem, "handle events" );
283                 }
284         },
286         dispatch: function( nativeEvent ) {
288                 var i, j, ret, matched, handleObj, handlerQueue,
289                         args = new Array( arguments.length ),
291                         // Make a writable jQuery.Event from the native event object
292                         event = jQuery.event.fix( nativeEvent ),
294                         handlers = (
295                                 dataPriv.get( this, "events" ) || Object.create( null )
296                         )[ event.type ] || [],
297                         special = jQuery.event.special[ event.type ] || {};
299                 // Use the fix-ed jQuery.Event rather than the (read-only) native event
300                 args[ 0 ] = event;
302                 for ( i = 1; i < arguments.length; i++ ) {
303                         args[ i ] = arguments[ i ];
304                 }
306                 event.delegateTarget = this;
308                 // Call the preDispatch hook for the mapped type, and let it bail if desired
309                 if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
310                         return;
311                 }
313                 // Determine handlers
314                 handlerQueue = jQuery.event.handlers.call( this, event, handlers );
316                 // Run delegates first; they may want to stop propagation beneath us
317                 i = 0;
318                 while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) {
319                         event.currentTarget = matched.elem;
321                         j = 0;
322                         while ( ( handleObj = matched.handlers[ j++ ] ) &&
323                                 !event.isImmediatePropagationStopped() ) {
325                                 // If the event is namespaced, then each handler is only invoked if it is
326                                 // specially universal or its namespaces are a superset of the event's.
327                                 if ( !event.rnamespace || handleObj.namespace === false ||
328                                         event.rnamespace.test( handleObj.namespace ) ) {
330                                         event.handleObj = handleObj;
331                                         event.data = handleObj.data;
333                                         ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle ||
334                                                 handleObj.handler ).apply( matched.elem, args );
336                                         if ( ret !== undefined ) {
337                                                 if ( ( event.result = ret ) === false ) {
338                                                         event.preventDefault();
339                                                         event.stopPropagation();
340                                                 }
341                                         }
342                                 }
343                         }
344                 }
346                 // Call the postDispatch hook for the mapped type
347                 if ( special.postDispatch ) {
348                         special.postDispatch.call( this, event );
349                 }
351                 return event.result;
352         },
354         handlers: function( event, handlers ) {
355                 var i, handleObj, sel, matchedHandlers, matchedSelectors,
356                         handlerQueue = [],
357                         delegateCount = handlers.delegateCount,
358                         cur = event.target;
360                 // Find delegate handlers
361                 if ( delegateCount &&
363                         // Support: Firefox <=42 - 66+
364                         // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861)
365                         // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click
366                         // Support: IE 11+
367                         // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343)
368                         !( event.type === "click" && event.button >= 1 ) ) {
370                         for ( ; cur !== this; cur = cur.parentNode || this ) {
372                                 // Don't check non-elements (#13208)
373                                 // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
374                                 if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) {
375                                         matchedHandlers = [];
376                                         matchedSelectors = {};
377                                         for ( i = 0; i < delegateCount; i++ ) {
378                                                 handleObj = handlers[ i ];
380                                                 // Don't conflict with Object.prototype properties (#13203)
381                                                 sel = handleObj.selector + " ";
383                                                 if ( matchedSelectors[ sel ] === undefined ) {
384                                                         matchedSelectors[ sel ] = handleObj.needsContext ?
385                                                                 jQuery( sel, this ).index( cur ) > -1 :
386                                                                 jQuery.find( sel, this, null, [ cur ] ).length;
387                                                 }
388                                                 if ( matchedSelectors[ sel ] ) {
389                                                         matchedHandlers.push( handleObj );
390                                                 }
391                                         }
392                                         if ( matchedHandlers.length ) {
393                                                 handlerQueue.push( { elem: cur, handlers: matchedHandlers } );
394                                         }
395                                 }
396                         }
397                 }
399                 // Add the remaining (directly-bound) handlers
400                 cur = this;
401                 if ( delegateCount < handlers.length ) {
402                         handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } );
403                 }
405                 return handlerQueue;
406         },
408         addProp: function( name, hook ) {
409                 Object.defineProperty( jQuery.Event.prototype, name, {
410                         enumerable: true,
411                         configurable: true,
413                         get: typeof hook === "function" ?
414                                 function() {
415                                         if ( this.originalEvent ) {
416                                                 return hook( this.originalEvent );
417                                         }
418                                 } :
419                                 function() {
420                                         if ( this.originalEvent ) {
421                                                 return this.originalEvent[ name ];
422                                         }
423                                 },
425                         set: function( value ) {
426                                 Object.defineProperty( this, name, {
427                                         enumerable: true,
428                                         configurable: true,
429                                         writable: true,
430                                         value: value
431                                 } );
432                         }
433                 } );
434         },
436         fix: function( originalEvent ) {
437                 return originalEvent[ jQuery.expando ] ?
438                         originalEvent :
439                         new jQuery.Event( originalEvent );
440         },
442         special: {
443                 load: {
445                         // Prevent triggered image.load events from bubbling to window.load
446                         noBubble: true
447                 },
448                 click: {
450                         // Utilize native event to ensure correct state for checkable inputs
451                         setup: function( data ) {
453                                 // For mutual compressibility with _default, replace `this` access with a local var.
454                                 // `|| data` is dead code meant only to preserve the variable through minification.
455                                 var el = this || data;
457                                 // Claim the first handler
458                                 if ( rcheckableType.test( el.type ) &&
459                                         el.click && nodeName( el, "input" ) ) {
461                                         // dataPriv.set( el, "click", ... )
462                                         leverageNative( el, "click", returnTrue );
463                                 }
465                                 // Return false to allow normal processing in the caller
466                                 return false;
467                         },
468                         trigger: function( data ) {
470                                 // For mutual compressibility with _default, replace `this` access with a local var.
471                                 // `|| data` is dead code meant only to preserve the variable through minification.
472                                 var el = this || data;
474                                 // Force setup before triggering a click
475                                 if ( rcheckableType.test( el.type ) &&
476                                         el.click && nodeName( el, "input" ) ) {
478                                         leverageNative( el, "click" );
479                                 }
481                                 // Return non-false to allow normal event-path propagation
482                                 return true;
483                         },
485                         // For cross-browser consistency, suppress native .click() on links
486                         // Also prevent it if we're currently inside a leveraged native-event stack
487                         _default: function( event ) {
488                                 var target = event.target;
489                                 return rcheckableType.test( target.type ) &&
490                                         target.click && nodeName( target, "input" ) &&
491                                         dataPriv.get( target, "click" ) ||
492                                         nodeName( target, "a" );
493                         }
494                 },
496                 beforeunload: {
497                         postDispatch: function( event ) {
499                                 // Support: Chrome <=73+
500                                 // Chrome doesn't alert on `event.preventDefault()`
501                                 // as the standard mandates.
502                                 if ( event.result !== undefined && event.originalEvent ) {
503                                         event.originalEvent.returnValue = event.result;
504                                 }
505                         }
506                 }
507         }
510 // Ensure the presence of an event listener that handles manually-triggered
511 // synthetic events by interrupting progress until reinvoked in response to
512 // *native* events that it fires directly, ensuring that state changes have
513 // already occurred before other listeners are invoked.
514 function leverageNative( el, type, expectSync ) {
516         // Missing expectSync indicates a trigger call, which must force setup through jQuery.event.add
517         if ( !expectSync ) {
518                 if ( dataPriv.get( el, type ) === undefined ) {
519                         jQuery.event.add( el, type, returnTrue );
520                 }
521                 return;
522         }
524         // Register the controller as a special universal handler for all event namespaces
525         dataPriv.set( el, type, false );
526         jQuery.event.add( el, type, {
527                 namespace: false,
528                 handler: function( event ) {
529                         var notAsync, result,
530                                 saved = dataPriv.get( this, type );
532                         if ( ( event.isTrigger & 1 ) && this[ type ] ) {
534                                 // Interrupt processing of the outer synthetic .trigger()ed event
535                                 // Saved data should be false in such cases, but might be a leftover capture object
536                                 // from an async native handler (gh-4350)
537                                 if ( !saved.length ) {
539                                         // Store arguments for use when handling the inner native event
540                                         // There will always be at least one argument (an event object), so this array
541                                         // will not be confused with a leftover capture object.
542                                         saved = slice.call( arguments );
543                                         dataPriv.set( this, type, saved );
545                                         // Trigger the native event and capture its result
546                                         // Support: IE <=9 - 11+
547                                         // focus() and blur() are asynchronous
548                                         notAsync = expectSync( this, type );
549                                         this[ type ]();
550                                         result = dataPriv.get( this, type );
551                                         if ( saved !== result || notAsync ) {
552                                                 dataPriv.set( this, type, false );
553                                         } else {
554                                                 result = {};
555                                         }
556                                         if ( saved !== result ) {
558                                                 // Cancel the outer synthetic event
559                                                 event.stopImmediatePropagation();
560                                                 event.preventDefault();
562                                                 // Support: Chrome 86+
563                                                 // In Chrome, if an element having a focusout handler is blurred by
564                                                 // clicking outside of it, it invokes the handler synchronously. If
565                                                 // that handler calls `.remove()` on the element, the data is cleared,
566                                                 // leaving `result` undefined. We need to guard against this.
567                                                 return result && result.value;
568                                         }
570                                 // If this is an inner synthetic event for an event with a bubbling surrogate
571                                 // (focus or blur), assume that the surrogate already propagated from triggering the
572                                 // native event and prevent that from happening again here.
573                                 // This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the
574                                 // bubbling surrogate propagates *after* the non-bubbling base), but that seems
575                                 // less bad than duplication.
576                                 } else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) {
577                                         event.stopPropagation();
578                                 }
580                         // If this is a native event triggered above, everything is now in order
581                         // Fire an inner synthetic event with the original arguments
582                         } else if ( saved.length ) {
584                                 // ...and capture the result
585                                 dataPriv.set( this, type, {
586                                         value: jQuery.event.trigger(
588                                                 // Support: IE <=9 - 11+
589                                                 // Extend with the prototype to reset the above stopImmediatePropagation()
590                                                 jQuery.extend( saved[ 0 ], jQuery.Event.prototype ),
591                                                 saved.slice( 1 ),
592                                                 this
593                                         )
594                                 } );
596                                 // Abort handling of the native event
597                                 event.stopImmediatePropagation();
598                         }
599                 }
600         } );
603 jQuery.removeEvent = function( elem, type, handle ) {
605         // This "if" is needed for plain objects
606         if ( elem.removeEventListener ) {
607                 elem.removeEventListener( type, handle );
608         }
611 jQuery.Event = function( src, props ) {
613         // Allow instantiation without the 'new' keyword
614         if ( !( this instanceof jQuery.Event ) ) {
615                 return new jQuery.Event( src, props );
616         }
618         // Event object
619         if ( src && src.type ) {
620                 this.originalEvent = src;
621                 this.type = src.type;
623                 // Events bubbling up the document may have been marked as prevented
624                 // by a handler lower down the tree; reflect the correct value.
625                 this.isDefaultPrevented = src.defaultPrevented ?
626                         returnTrue :
627                         returnFalse;
629                 // Create target properties
630                 this.target = src.target;
631                 this.currentTarget = src.currentTarget;
632                 this.relatedTarget = src.relatedTarget;
634         // Event type
635         } else {
636                 this.type = src;
637         }
639         // Put explicitly provided properties onto the event object
640         if ( props ) {
641                 jQuery.extend( this, props );
642         }
644         // Create a timestamp if incoming event doesn't have one
645         this.timeStamp = src && src.timeStamp || Date.now();
647         // Mark it as fixed
648         this[ jQuery.expando ] = true;
651 // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
652 // https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
653 jQuery.Event.prototype = {
654         constructor: jQuery.Event,
655         isDefaultPrevented: returnFalse,
656         isPropagationStopped: returnFalse,
657         isImmediatePropagationStopped: returnFalse,
658         isSimulated: false,
660         preventDefault: function() {
661                 var e = this.originalEvent;
663                 this.isDefaultPrevented = returnTrue;
665                 if ( e && !this.isSimulated ) {
666                         e.preventDefault();
667                 }
668         },
669         stopPropagation: function() {
670                 var e = this.originalEvent;
672                 this.isPropagationStopped = returnTrue;
674                 if ( e && !this.isSimulated ) {
675                         e.stopPropagation();
676                 }
677         },
678         stopImmediatePropagation: function() {
679                 var e = this.originalEvent;
681                 this.isImmediatePropagationStopped = returnTrue;
683                 if ( e && !this.isSimulated ) {
684                         e.stopImmediatePropagation();
685                 }
687                 this.stopPropagation();
688         }
691 // Includes all common event props including KeyEvent and MouseEvent specific props
692 jQuery.each( {
693         altKey: true,
694         bubbles: true,
695         cancelable: true,
696         changedTouches: true,
697         ctrlKey: true,
698         detail: true,
699         eventPhase: true,
700         metaKey: true,
701         pageX: true,
702         pageY: true,
703         shiftKey: true,
704         view: true,
705         "char": true,
706         code: true,
707         charCode: true,
708         key: true,
709         keyCode: true,
710         button: true,
711         buttons: true,
712         clientX: true,
713         clientY: true,
714         offsetX: true,
715         offsetY: true,
716         pointerId: true,
717         pointerType: true,
718         screenX: true,
719         screenY: true,
720         targetTouches: true,
721         toElement: true,
722         touches: true,
723         which: true
724 }, jQuery.event.addProp );
726 jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) {
727         jQuery.event.special[ type ] = {
729                 // Utilize native event if possible so blur/focus sequence is correct
730                 setup: function() {
732                         // Claim the first handler
733                         // dataPriv.set( this, "focus", ... )
734                         // dataPriv.set( this, "blur", ... )
735                         leverageNative( this, type, expectSync );
737                         // Return false to allow normal processing in the caller
738                         return false;
739                 },
740                 trigger: function() {
742                         // Force setup before trigger
743                         leverageNative( this, type );
745                         // Return non-false to allow normal event-path propagation
746                         return true;
747                 },
749                 // Suppress native focus or blur if we're currently inside
750                 // a leveraged native-event stack
751                 _default: function( event ) {
752                         return dataPriv.get( event.target, type );
753                 },
755                 delegateType: delegateType
756         };
757 } );
759 // Create mouseenter/leave events using mouseover/out and event-time checks
760 // so that event delegation works in jQuery.
761 // Do the same for pointerenter/pointerleave and pointerover/pointerout
762 jQuery.each( {
763         mouseenter: "mouseover",
764         mouseleave: "mouseout",
765         pointerenter: "pointerover",
766         pointerleave: "pointerout"
767 }, function( orig, fix ) {
768         jQuery.event.special[ orig ] = {
769                 delegateType: fix,
770                 bindType: fix,
772                 handle: function( event ) {
773                         var ret,
774                                 target = this,
775                                 related = event.relatedTarget,
776                                 handleObj = event.handleObj;
778                         // For mouseenter/leave call the handler if related is outside the target.
779                         // NB: No relatedTarget if the mouse left/entered the browser window
780                         if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) {
781                                 event.type = handleObj.origType;
782                                 ret = handleObj.handler.apply( this, arguments );
783                                 event.type = fix;
784                         }
785                         return ret;
786                 }
787         };
788 } );
790 jQuery.fn.extend( {
792         on: function( types, selector, data, fn ) {
793                 return on( this, types, selector, data, fn );
794         },
795         one: function( types, selector, data, fn ) {
796                 return on( this, types, selector, data, fn, 1 );
797         },
798         off: function( types, selector, fn ) {
799                 var handleObj, type;
800                 if ( types && types.preventDefault && types.handleObj ) {
802                         // ( event )  dispatched jQuery.Event
803                         handleObj = types.handleObj;
804                         jQuery( types.delegateTarget ).off(
805                                 handleObj.namespace ?
806                                         handleObj.origType + "." + handleObj.namespace :
807                                         handleObj.origType,
808                                 handleObj.selector,
809                                 handleObj.handler
810                         );
811                         return this;
812                 }
813                 if ( typeof types === "object" ) {
815                         // ( types-object [, selector] )
816                         for ( type in types ) {
817                                 this.off( type, selector, types[ type ] );
818                         }
819                         return this;
820                 }
821                 if ( selector === false || typeof selector === "function" ) {
823                         // ( types [, fn] )
824                         fn = selector;
825                         selector = undefined;
826                 }
827                 if ( fn === false ) {
828                         fn = returnFalse;
829                 }
830                 return this.each( function() {
831                         jQuery.event.remove( this, types, fn, selector );
832                 } );
833         }
834 } );
836 export default jQuery;