Offset: allow offset setter to throw for disconnected elements
[jquery.git] / src / event.js
blobb371b9a272ccb267361ca31c1c3ea54062a9e5c3
1 define([
2         "./core",
3         "./var/document",
4         "./var/rnotwhite",
5         "./var/hasOwn",
6         "./var/slice",
7         "./event/support",
8         "./data/var/dataPriv",
10         "./core/init",
11         "./data/accepts",
12         "./selector"
13 ], function( jQuery, document, rnotwhite, hasOwn, slice, support, dataPriv ) {
15 var
16         rkeyEvent = /^key/,
17         rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/,
18         rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
19         rtypenamespace = /^([^.]*)(?:\.(.+)|)/;
21 function returnTrue() {
22         return true;
25 function returnFalse() {
26         return false;
29 // Support: IE9
30 // See #13393 for more info
31 function safeActiveElement() {
32         try {
33                 return document.activeElement;
34         } catch ( err ) { }
38  * Helper functions for managing events -- not part of the public interface.
39  * Props to Dean Edwards' addEvent library for many of the ideas.
40  */
41 jQuery.event = {
43         global: {},
45         add: function( elem, types, handler, data, selector ) {
47                 var handleObjIn, eventHandle, tmp,
48                         events, t, handleObj,
49                         special, handlers, type, namespaces, origType,
50                         elemData = dataPriv.get( elem );
52                 // Don't attach events to noData or text/comment nodes (but allow plain objects)
53                 if ( !elemData ) {
54                         return;
55                 }
57                 // Caller can pass in an object of custom data in lieu of the handler
58                 if ( handler.handler ) {
59                         handleObjIn = handler;
60                         handler = handleObjIn.handler;
61                         selector = handleObjIn.selector;
62                 }
64                 // Make sure that the handler has a unique ID, used to find/remove it later
65                 if ( !handler.guid ) {
66                         handler.guid = jQuery.guid++;
67                 }
69                 // Init the element's event structure and main handler, if this is the first
70                 if ( !(events = elemData.events) ) {
71                         events = elemData.events = {};
72                 }
73                 if ( !(eventHandle = elemData.handle) ) {
74                         eventHandle = elemData.handle = function( e ) {
75                                 // Discard the second event of a jQuery.event.trigger() and
76                                 // when an event is called after a page has unloaded
77                                 return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ?
78                                         jQuery.event.dispatch.apply( elem, arguments ) : undefined;
79                         };
80                 }
82                 // Handle multiple events separated by a space
83                 types = ( types || "" ).match( rnotwhite ) || [ "" ];
84                 t = types.length;
85                 while ( t-- ) {
86                         tmp = rtypenamespace.exec( types[t] ) || [];
87                         type = origType = tmp[1];
88                         namespaces = ( tmp[2] || "" ).split( "." ).sort();
90                         // There *must* be a type, no attaching namespace-only handlers
91                         if ( !type ) {
92                                 continue;
93                         }
95                         // If event changes its type, use the special event handlers for the changed type
96                         special = jQuery.event.special[ type ] || {};
98                         // If selector defined, determine special event api type, otherwise given type
99                         type = ( selector ? special.delegateType : special.bindType ) || type;
101                         // Update special based on newly reset type
102                         special = jQuery.event.special[ type ] || {};
104                         // handleObj is passed to all event handlers
105                         handleObj = jQuery.extend({
106                                 type: type,
107                                 origType: origType,
108                                 data: data,
109                                 handler: handler,
110                                 guid: handler.guid,
111                                 selector: selector,
112                                 needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
113                                 namespace: namespaces.join(".")
114                         }, handleObjIn );
116                         // Init the event handler queue if we're the first
117                         if ( !(handlers = events[ type ]) ) {
118                                 handlers = events[ type ] = [];
119                                 handlers.delegateCount = 0;
121                                 // Only use addEventListener if the special events handler returns false
122                                 if ( !special.setup ||
123                                         special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
125                                         if ( elem.addEventListener ) {
126                                                 elem.addEventListener( type, eventHandle );
127                                         }
128                                 }
129                         }
131                         if ( special.add ) {
132                                 special.add.call( elem, handleObj );
134                                 if ( !handleObj.handler.guid ) {
135                                         handleObj.handler.guid = handler.guid;
136                                 }
137                         }
139                         // Add to the element's handler list, delegates in front
140                         if ( selector ) {
141                                 handlers.splice( handlers.delegateCount++, 0, handleObj );
142                         } else {
143                                 handlers.push( handleObj );
144                         }
146                         // Keep track of which events have ever been used, for event optimization
147                         jQuery.event.global[ type ] = true;
148                 }
150         },
152         // Detach an event or set of events from an element
153         remove: function( elem, types, handler, selector, mappedTypes ) {
155                 var j, origCount, tmp,
156                         events, t, handleObj,
157                         special, handlers, type, namespaces, origType,
158                         elemData = dataPriv.hasData( elem ) && dataPriv.get( elem );
160                 if ( !elemData || !(events = elemData.events) ) {
161                         return;
162                 }
164                 // Once for each type.namespace in types; type may be omitted
165                 types = ( types || "" ).match( rnotwhite ) || [ "" ];
166                 t = types.length;
167                 while ( t-- ) {
168                         tmp = rtypenamespace.exec( types[t] ) || [];
169                         type = origType = tmp[1];
170                         namespaces = ( tmp[2] || "" ).split( "." ).sort();
172                         // Unbind all events (on this namespace, if provided) for the element
173                         if ( !type ) {
174                                 for ( type in events ) {
175                                         jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
176                                 }
177                                 continue;
178                         }
180                         special = jQuery.event.special[ type ] || {};
181                         type = ( selector ? special.delegateType : special.bindType ) || type;
182                         handlers = events[ type ] || [];
183                         tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" );
185                         // Remove matching events
186                         origCount = j = handlers.length;
187                         while ( j-- ) {
188                                 handleObj = handlers[ j ];
190                                 if ( ( mappedTypes || origType === handleObj.origType ) &&
191                                         ( !handler || handler.guid === handleObj.guid ) &&
192                                         ( !tmp || tmp.test( handleObj.namespace ) ) &&
193                                         ( !selector || selector === handleObj.selector ||
194                                                 selector === "**" && handleObj.selector ) ) {
195                                         handlers.splice( j, 1 );
197                                         if ( handleObj.selector ) {
198                                                 handlers.delegateCount--;
199                                         }
200                                         if ( special.remove ) {
201                                                 special.remove.call( elem, handleObj );
202                                         }
203                                 }
204                         }
206                         // Remove generic event handler if we removed something and no more handlers exist
207                         // (avoids potential for endless recursion during removal of special event handlers)
208                         if ( origCount && !handlers.length ) {
209                                 if ( !special.teardown ||
210                                         special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
212                                         jQuery.removeEvent( elem, type, elemData.handle );
213                                 }
215                                 delete events[ type ];
216                         }
217                 }
219                 // Remove the expando if it's no longer used
220                 if ( jQuery.isEmptyObject( events ) ) {
221                         // Normally this should go through the data api
222                         // but since event.js owns these properties,
223                         // this exception is made for the sake of optimizing
224                         // the operation.
225                         delete elemData.handle;
226                         delete elemData.events;
227                 }
228         },
230         trigger: function( event, data, elem, onlyHandlers ) {
232                 var i, cur, tmp, bubbleType, ontype, handle, special,
233                         eventPath = [ elem || document ],
234                         type = hasOwn.call( event, "type" ) ? event.type : event,
235                         namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : [];
237                 cur = tmp = elem = elem || document;
239                 // Don't do events on text and comment nodes
240                 if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
241                         return;
242                 }
244                 // focus/blur morphs to focusin/out; ensure we're not firing them right now
245                 if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
246                         return;
247                 }
249                 if ( type.indexOf(".") > -1 ) {
250                         // Namespaced trigger; create a regexp to match event type in handle()
251                         namespaces = type.split(".");
252                         type = namespaces.shift();
253                         namespaces.sort();
254                 }
255                 ontype = type.indexOf(":") < 0 && "on" + type;
257                 // Caller can pass in a jQuery.Event object, Object, or just an event type string
258                 event = event[ jQuery.expando ] ?
259                         event :
260                         new jQuery.Event( type, typeof event === "object" && event );
262                 // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)
263                 event.isTrigger = onlyHandlers ? 2 : 3;
264                 event.namespace = namespaces.join(".");
265                 event.rnamespace = event.namespace ?
266                         new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) :
267                         null;
269                 // Clean up the event in case it is being reused
270                 event.result = undefined;
271                 if ( !event.target ) {
272                         event.target = elem;
273                 }
275                 // Clone any incoming data and prepend the event, creating the handler arg list
276                 data = data == null ?
277                         [ event ] :
278                         jQuery.makeArray( data, [ event ] );
280                 // Allow special events to draw outside the lines
281                 special = jQuery.event.special[ type ] || {};
282                 if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
283                         return;
284                 }
286                 // Determine event propagation path in advance, per W3C events spec (#9951)
287                 // Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
288                 if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
290                         bubbleType = special.delegateType || type;
291                         if ( !rfocusMorph.test( bubbleType + type ) ) {
292                                 cur = cur.parentNode;
293                         }
294                         for ( ; cur; cur = cur.parentNode ) {
295                                 eventPath.push( cur );
296                                 tmp = cur;
297                         }
299                         // Only add window if we got to document (e.g., not plain obj or detached DOM)
300                         if ( tmp === (elem.ownerDocument || document) ) {
301                                 eventPath.push( tmp.defaultView || tmp.parentWindow || window );
302                         }
303                 }
305                 // Fire handlers on the event path
306                 i = 0;
307                 while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) {
309                         event.type = i > 1 ?
310                                 bubbleType :
311                                 special.bindType || type;
313                         // jQuery handler
314                         handle = ( dataPriv.get( cur, "events" ) || {} )[ event.type ] &&
315                                 dataPriv.get( cur, "handle" );
316                         if ( handle ) {
317                                 handle.apply( cur, data );
318                         }
320                         // Native handler
321                         handle = ontype && cur[ ontype ];
322                         if ( handle && handle.apply && jQuery.acceptData( cur ) ) {
323                                 event.result = handle.apply( cur, data );
324                                 if ( event.result === false ) {
325                                         event.preventDefault();
326                                 }
327                         }
328                 }
329                 event.type = type;
331                 // If nobody prevented the default action, do it now
332                 if ( !onlyHandlers && !event.isDefaultPrevented() ) {
334                         if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) &&
335                                 jQuery.acceptData( elem ) ) {
337                                 // Call a native DOM method on the target with the same name name as the event.
338                                 // Don't do default actions on window, that's where global variables be (#6170)
339                                 if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) {
341                                         // Don't re-trigger an onFOO event when we call its FOO() method
342                                         tmp = elem[ ontype ];
344                                         if ( tmp ) {
345                                                 elem[ ontype ] = null;
346                                         }
348                                         // Prevent re-triggering of the same event, since we already bubbled it above
349                                         jQuery.event.triggered = type;
350                                         elem[ type ]();
351                                         jQuery.event.triggered = undefined;
353                                         if ( tmp ) {
354                                                 elem[ ontype ] = tmp;
355                                         }
356                                 }
357                         }
358                 }
360                 return event.result;
361         },
363         dispatch: function( event ) {
365                 // Make a writable jQuery.Event from the native event object
366                 event = jQuery.event.fix( event );
368                 var i, j, ret, matched, handleObj,
369                         handlerQueue = [],
370                         args = slice.call( arguments ),
371                         handlers = ( dataPriv.get( this, "events" ) || {} )[ event.type ] || [],
372                         special = jQuery.event.special[ event.type ] || {};
374                 // Use the fix-ed jQuery.Event rather than the (read-only) native event
375                 args[0] = event;
376                 event.delegateTarget = this;
378                 // Call the preDispatch hook for the mapped type, and let it bail if desired
379                 if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
380                         return;
381                 }
383                 // Determine handlers
384                 handlerQueue = jQuery.event.handlers.call( this, event, handlers );
386                 // Run delegates first; they may want to stop propagation beneath us
387                 i = 0;
388                 while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) {
389                         event.currentTarget = matched.elem;
391                         j = 0;
392                         while ( (handleObj = matched.handlers[ j++ ]) &&
393                                 !event.isImmediatePropagationStopped() ) {
395                                 // Triggered event must either 1) have no namespace, or 2) have namespace(s)
396                                 // a subset or equal to those in the bound event (both can have no namespace).
397                                 if ( !event.rnamespace || event.rnamespace.test( handleObj.namespace ) ) {
399                                         event.handleObj = handleObj;
400                                         event.data = handleObj.data;
402                                         ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle ||
403                                                 handleObj.handler ).apply( matched.elem, args );
405                                         if ( ret !== undefined ) {
406                                                 if ( (event.result = ret) === false ) {
407                                                         event.preventDefault();
408                                                         event.stopPropagation();
409                                                 }
410                                         }
411                                 }
412                         }
413                 }
415                 // Call the postDispatch hook for the mapped type
416                 if ( special.postDispatch ) {
417                         special.postDispatch.call( this, event );
418                 }
420                 return event.result;
421         },
423         handlers: function( event, handlers ) {
424                 var i, matches, sel, handleObj,
425                         handlerQueue = [],
426                         delegateCount = handlers.delegateCount,
427                         cur = event.target;
429                 // Support (at least): Chrome, IE9
430                 // Find delegate handlers
431                 // Black-hole SVG <use> instance trees (#13180)
432                 //
433                 // Support: Firefox
434                 // Avoid non-left-click bubbling in Firefox (#3861)
435                 if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) {
437                         for ( ; cur !== this; cur = cur.parentNode || this ) {
439                                 // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
440                                 if ( cur.disabled !== true || event.type !== "click" ) {
441                                         matches = [];
442                                         for ( i = 0; i < delegateCount; i++ ) {
443                                                 handleObj = handlers[ i ];
445                                                 // Don't conflict with Object.prototype properties (#13203)
446                                                 sel = handleObj.selector + " ";
448                                                 if ( matches[ sel ] === undefined ) {
449                                                         matches[ sel ] = handleObj.needsContext ?
450                                                                 jQuery( sel, this ).index( cur ) > -1 :
451                                                                 jQuery.find( sel, this, null, [ cur ] ).length;
452                                                 }
453                                                 if ( matches[ sel ] ) {
454                                                         matches.push( handleObj );
455                                                 }
456                                         }
457                                         if ( matches.length ) {
458                                                 handlerQueue.push({ elem: cur, handlers: matches });
459                                         }
460                                 }
461                         }
462                 }
464                 // Add the remaining (directly-bound) handlers
465                 if ( delegateCount < handlers.length ) {
466                         handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) });
467                 }
469                 return handlerQueue;
470         },
472         // Includes some event props shared by KeyEvent and MouseEvent
473         props: ( "altKey bubbles cancelable ctrlKey currentTarget detail eventPhase " +
474                 "metaKey relatedTarget shiftKey target timeStamp view which" ).split(" "),
476         fixHooks: {},
478         keyHooks: {
479                 props: "char charCode key keyCode".split(" "),
480                 filter: function( event, original ) {
482                         // Add which for key events
483                         if ( event.which == null ) {
484                                 event.which = original.charCode != null ? original.charCode : original.keyCode;
485                         }
487                         return event;
488                 }
489         },
491         mouseHooks: {
492                 props: ( "button buttons clientX clientY offsetX offsetY pageX pageY " +
493                         "screenX screenY toElement" ).split(" "),
494                 filter: function( event, original ) {
495                         var eventDoc, doc, body,
496                                 button = original.button;
498                         // Calculate pageX/Y if missing and clientX/Y available
499                         if ( event.pageX == null && original.clientX != null ) {
500                                 eventDoc = event.target.ownerDocument || document;
501                                 doc = eventDoc.documentElement;
502                                 body = eventDoc.body;
504                                 event.pageX = original.clientX +
505                                         ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) -
506                                         ( doc && doc.clientLeft || body && body.clientLeft || 0 );
507                                 event.pageY = original.clientY +
508                                         ( doc && doc.scrollTop  || body && body.scrollTop  || 0 ) -
509                                         ( doc && doc.clientTop  || body && body.clientTop  || 0 );
510                         }
512                         // Add which for click: 1 === left; 2 === middle; 3 === right
513                         // Note: button is not normalized, so don't use it
514                         if ( !event.which && button !== undefined ) {
515                                 event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
516                         }
518                         return event;
519                 }
520         },
522         fix: function( event ) {
523                 if ( event[ jQuery.expando ] ) {
524                         return event;
525                 }
527                 // Create a writable copy of the event object and normalize some properties
528                 var i, prop, copy,
529                         type = event.type,
530                         originalEvent = event,
531                         fixHook = this.fixHooks[ type ];
533                 if ( !fixHook ) {
534                         this.fixHooks[ type ] = fixHook =
535                                 rmouseEvent.test( type ) ? this.mouseHooks :
536                                 rkeyEvent.test( type ) ? this.keyHooks :
537                                 {};
538                 }
539                 copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
541                 event = new jQuery.Event( originalEvent );
543                 i = copy.length;
544                 while ( i-- ) {
545                         prop = copy[ i ];
546                         event[ prop ] = originalEvent[ prop ];
547                 }
549                 // Support: Safari 6.0+
550                 // Target should not be a text node (#504, #13143)
551                 if ( event.target.nodeType === 3 ) {
552                         event.target = event.target.parentNode;
553                 }
555                 return fixHook.filter ? fixHook.filter( event, originalEvent ) : event;
556         },
558         special: {
559                 load: {
560                         // Prevent triggered image.load events from bubbling to window.load
561                         noBubble: true
562                 },
563                 focus: {
564                         // Fire native event if possible so blur/focus sequence is correct
565                         trigger: function() {
566                                 if ( this !== safeActiveElement() && this.focus ) {
567                                         this.focus();
568                                         return false;
569                                 }
570                         },
571                         delegateType: "focusin"
572                 },
573                 blur: {
574                         trigger: function() {
575                                 if ( this === safeActiveElement() && this.blur ) {
576                                         this.blur();
577                                         return false;
578                                 }
579                         },
580                         delegateType: "focusout"
581                 },
582                 click: {
583                         // For checkbox, fire native event so checked state will be right
584                         trigger: function() {
585                                 if ( this.type === "checkbox" && this.click && jQuery.nodeName( this, "input" ) ) {
586                                         this.click();
587                                         return false;
588                                 }
589                         },
591                         // For cross-browser consistency, don't fire native .click() on links
592                         _default: function( event ) {
593                                 return jQuery.nodeName( event.target, "a" );
594                         }
595                 },
597                 beforeunload: {
598                         postDispatch: function( event ) {
600                                 // Support: Firefox 20+
601                                 // Firefox doesn't alert if the returnValue field is not set.
602                                 if ( event.result !== undefined && event.originalEvent ) {
603                                         event.originalEvent.returnValue = event.result;
604                                 }
605                         }
606                 }
607         },
609         simulate: function( type, elem, event, bubble ) {
610                 // Piggyback on a donor event to simulate a different one.
611                 // Fake originalEvent to avoid donor's stopPropagation, but if the
612                 // simulated event prevents default then we do the same on the donor.
613                 var e = jQuery.extend(
614                         new jQuery.Event(),
615                         event,
616                         {
617                                 type: type,
618                                 isSimulated: true,
619                                 originalEvent: {}
620                         }
621                 );
622                 if ( bubble ) {
623                         jQuery.event.trigger( e, null, elem );
624                 } else {
625                         jQuery.event.dispatch.call( elem, e );
626                 }
627                 if ( e.isDefaultPrevented() ) {
628                         event.preventDefault();
629                 }
630         }
633 jQuery.removeEvent = function( elem, type, handle ) {
635         // This "if" is needed for plain objects
636         if ( elem.removeEventListener ) {
637                 elem.removeEventListener( type, handle );
638         }
641 jQuery.Event = function( src, props ) {
642         // Allow instantiation without the 'new' keyword
643         if ( !(this instanceof jQuery.Event) ) {
644                 return new jQuery.Event( src, props );
645         }
647         // Event object
648         if ( src && src.type ) {
649                 this.originalEvent = src;
650                 this.type = src.type;
652                 // Events bubbling up the document may have been marked as prevented
653                 // by a handler lower down the tree; reflect the correct value.
654                 this.isDefaultPrevented = src.defaultPrevented ||
655                                 src.defaultPrevented === undefined &&
656                                 // Support: Android<4.0
657                                 src.returnValue === false ?
658                         returnTrue :
659                         returnFalse;
661         // Event type
662         } else {
663                 this.type = src;
664         }
666         // Put explicitly provided properties onto the event object
667         if ( props ) {
668                 jQuery.extend( this, props );
669         }
671         // Create a timestamp if incoming event doesn't have one
672         this.timeStamp = src && src.timeStamp || jQuery.now();
674         // Mark it as fixed
675         this[ jQuery.expando ] = true;
678 // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
679 // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
680 jQuery.Event.prototype = {
681         constructor: jQuery.Event,
682         isDefaultPrevented: returnFalse,
683         isPropagationStopped: returnFalse,
684         isImmediatePropagationStopped: returnFalse,
686         preventDefault: function() {
687                 var e = this.originalEvent;
689                 this.isDefaultPrevented = returnTrue;
691                 if ( e ) {
692                         e.preventDefault();
693                 }
694         },
695         stopPropagation: function() {
696                 var e = this.originalEvent;
698                 this.isPropagationStopped = returnTrue;
700                 if ( e ) {
701                         e.stopPropagation();
702                 }
703         },
704         stopImmediatePropagation: function() {
705                 var e = this.originalEvent;
707                 this.isImmediatePropagationStopped = returnTrue;
709                 if ( e ) {
710                         e.stopImmediatePropagation();
711                 }
713                 this.stopPropagation();
714         }
717 // Create mouseenter/leave events using mouseover/out and event-time checks
718 // so that event delegation works in jQuery.
719 // Do the same for pointerenter/pointerleave and pointerover/pointerout
721 // Support: Safari<7.0
722 // Safari doesn't support mouseenter/mouseleave at all.
724 // Support: Chrome 34+
725 // Mouseenter doesn't perform while left mouse button is pressed
726 // (and initiated outside the observed element)
727 // https://code.google.com/p/chromium/issues/detail?id=333868
728 jQuery.each({
729         mouseenter: "mouseover",
730         mouseleave: "mouseout",
731         pointerenter: "pointerover",
732         pointerleave: "pointerout"
733 }, function( orig, fix ) {
734         jQuery.event.special[ orig ] = {
735                 delegateType: fix,
736                 bindType: fix,
738                 handle: function( event ) {
739                         var ret,
740                                 target = this,
741                                 related = event.relatedTarget,
742                                 handleObj = event.handleObj;
744                         // For mousenter/leave call the handler if related is outside the target.
745                         // NB: No relatedTarget if the mouse left/entered the browser window
746                         if ( !related || (related !== target && !jQuery.contains( target, related )) ) {
747                                 event.type = handleObj.origType;
748                                 ret = handleObj.handler.apply( this, arguments );
749                                 event.type = fix;
750                         }
751                         return ret;
752                 }
753         };
756 // Support: Firefox, Chrome, Safari
757 // Create "bubbling" focus and blur events
758 if ( !support.focusinBubbles ) {
759         jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
761                 // Attach a single capturing handler on the document while someone wants focusin/focusout
762                 var handler = function( event ) {
763                                 jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
764                         };
766                 jQuery.event.special[ fix ] = {
767                         setup: function() {
768                                 var doc = this.ownerDocument || this,
769                                         attaches = dataPriv.access( doc, fix );
771                                 if ( !attaches ) {
772                                         doc.addEventListener( orig, handler, true );
773                                 }
774                                 dataPriv.access( doc, fix, ( attaches || 0 ) + 1 );
775                         },
776                         teardown: function() {
777                                 var doc = this.ownerDocument || this,
778                                         attaches = dataPriv.access( doc, fix ) - 1;
780                                 if ( !attaches ) {
781                                         doc.removeEventListener( orig, handler, true );
782                                         dataPriv.remove( doc, fix );
784                                 } else {
785                                         dataPriv.access( doc, fix, attaches );
786                                 }
787                         }
788                 };
789         });
792 jQuery.fn.extend({
794         on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
795                 var origFn, type;
797                 // Types can be a map of types/handlers
798                 if ( typeof types === "object" ) {
799                         // ( types-Object, selector, data )
800                         if ( typeof selector !== "string" ) {
801                                 // ( types-Object, data )
802                                 data = data || selector;
803                                 selector = undefined;
804                         }
805                         for ( type in types ) {
806                                 this.on( type, selector, data, types[ type ], one );
807                         }
808                         return this;
809                 }
811                 if ( data == null && fn == null ) {
812                         // ( types, fn )
813                         fn = selector;
814                         data = selector = undefined;
815                 } else if ( fn == null ) {
816                         if ( typeof selector === "string" ) {
817                                 // ( types, selector, fn )
818                                 fn = data;
819                                 data = undefined;
820                         } else {
821                                 // ( types, data, fn )
822                                 fn = data;
823                                 data = selector;
824                                 selector = undefined;
825                         }
826                 }
827                 if ( fn === false ) {
828                         fn = returnFalse;
829                 }
831                 if ( one === 1 ) {
832                         origFn = fn;
833                         fn = function( event ) {
834                                 // Can use an empty set, since event contains the info
835                                 jQuery().off( event );
836                                 return origFn.apply( this, arguments );
837                         };
838                         // Use same guid so caller can remove using origFn
839                         fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
840                 }
841                 return this.each( function() {
842                         jQuery.event.add( this, types, fn, data, selector );
843                 });
844         },
845         one: function( types, selector, data, fn ) {
846                 return this.on( types, selector, data, fn, 1 );
847         },
848         off: function( types, selector, fn ) {
849                 var handleObj, type;
850                 if ( types && types.preventDefault && types.handleObj ) {
851                         // ( event )  dispatched jQuery.Event
852                         handleObj = types.handleObj;
853                         jQuery( types.delegateTarget ).off(
854                                 handleObj.namespace ?
855                                         handleObj.origType + "." + handleObj.namespace :
856                                         handleObj.origType,
857                                 handleObj.selector,
858                                 handleObj.handler
859                         );
860                         return this;
861                 }
862                 if ( typeof types === "object" ) {
863                         // ( types-object [, selector] )
864                         for ( type in types ) {
865                                 this.off( type, selector, types[ type ] );
866                         }
867                         return this;
868                 }
869                 if ( selector === false || typeof selector === "function" ) {
870                         // ( types [, fn] )
871                         fn = selector;
872                         selector = undefined;
873                 }
874                 if ( fn === false ) {
875                         fn = returnFalse;
876                 }
877                 return this.each(function() {
878                         jQuery.event.remove( this, types, fn, selector );
879                 });
880         },
882         trigger: function( type, data ) {
883                 return this.each(function() {
884                         jQuery.event.trigger( type, data, this );
885                 });
886         },
887         triggerHandler: function( type, data ) {
888                 var elem = this[0];
889                 if ( elem ) {
890                         return jQuery.event.trigger( type, data, elem, true );
891                 }
892         }
895 return jQuery;