UNPKG

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