Make sure that mousing over Chrome "internal div" elements results in no trigger...
[jquery.git] / src / event.js
index 22c9585..a17cf89 100644 (file)
@@ -7,8 +7,7 @@ var rnamespaces = /\.(.*)$/,
        rescape = /[^\w\s.|`]/g,
        fcleanup = function( nm ) {
                return nm.replace(rescape, "\\$&");
-       },
-       focusCounts = { focusin: 0, focusout: 0 };
+       };
 
 /*
  * A number of helper functions used for managing events.
@@ -50,7 +49,7 @@ jQuery.event = {
                }
 
                // Init the element's event structure
-               var elemData = jQuery.data( elem );
+               var elemData = jQuery._data( elem );
 
                // If no elemData is found then we must be trying to bind to one of the
                // banned noData elements
@@ -58,26 +57,10 @@ jQuery.event = {
                        return;
                }
 
-               // Use a key less likely to result in collisions for plain JS objects.
-               // Fixes bug #7150.
-               var eventKey = elem.nodeType ? "events" : "__events__",
-                       events = elemData[ eventKey ],
+               var events = elemData.events,
                        eventHandle = elemData.handle;
 
-               if ( typeof events === "function" ) {
-                       // On plain objects events is a fn that holds the the data
-                       // which prevents this data from being JSON serialized
-                       // the function does not need to be called, it just contains the data
-                       eventHandle = events.handle;
-                       events = events.events;
-
-               } else if ( !events ) {
-                       if ( !elem.nodeType ) {
-                               // On plain objects, create a fn that acts as the holder
-                               // of the values to avoid JSON serialization of event data
-                               elemData[ eventKey ] = elemData = function(){};
-                       }
-
+               if ( !events ) {
                        elemData.events = events = {};
                }
 
@@ -177,19 +160,13 @@ jQuery.event = {
                }
 
                var ret, type, fn, j, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType,
-                       eventKey = elem.nodeType ? "events" : "__events__",
-                       elemData = jQuery.data( elem ),
-                       events = elemData && elemData[ eventKey ];
+                       elemData = jQuery.hasData( elem ) && jQuery._data( elem ),
+                       events = elemData && elemData.events;
 
                if ( !elemData || !events ) {
                        return;
                }
 
-               if ( typeof events === "function" ) {
-                       elemData = events;
-                       events = events.events;
-               }
-
                // types is actually an event object here
                if ( types && types.type ) {
                        handler = types.handler;
@@ -289,11 +266,8 @@ jQuery.event = {
                        delete elemData.events;
                        delete elemData.handle;
 
-                       if ( typeof elemData === "function" ) {
-                               jQuery.removeData( elem, eventKey );
-
-                       } else if ( jQuery.isEmptyObject( elemData ) ) {
-                               jQuery.removeData( elem );
+                       if ( jQuery.isEmptyObject( elemData ) ) {
+                               jQuery.removeData( elem, undefined, true );
                        }
                }
        },
@@ -325,9 +299,16 @@ jQuery.event = {
 
                                // Only trigger if we've ever bound an event for it
                                if ( jQuery.event.global[ type ] ) {
+                                       // XXX This code smells terrible. event.js should not be directly
+                                       // inspecting the data cache
                                        jQuery.each( jQuery.cache, function() {
-                                               if ( this.events && this.events[type] ) {
-                                                       jQuery.event.trigger( event, data, this.handle.elem );
+                                               // internalKey variable is just used to make it easier to find
+                                               // and potentially change this stuff later; currently it just
+                                               // points to jQuery.expando
+                                               var internalKey = jQuery.expando,
+                                                       internalCache = this[ internalKey ];
+                                               if ( internalCache && internalCache.events && internalCache.events[ type ] ) {
+                                                       jQuery.event.trigger( event, data, internalCache.handle.elem );
                                                }
                                        });
                                }
@@ -352,9 +333,7 @@ jQuery.event = {
                event.currentTarget = elem;
 
                // Trigger the event, it is assumed that "handle" is a function
-               var handle = elem.nodeType ?
-                       jQuery.data( elem, "handle" ) :
-                       (jQuery.data( elem, "__events__" ) || {}).handle;
+               var handle = jQuery._data( elem, "handle" );
 
                if ( handle ) {
                        handle.apply( elem, data );
@@ -432,11 +411,7 @@ jQuery.event = {
 
                event.namespace = event.namespace || namespace_sort.join(".");
 
-               events = jQuery.data(this, this.nodeType ? "events" : "__events__");
-
-               if ( typeof events === "function" ) {
-                       events = events.events;
-               }
+               events = jQuery._data(this, "events");
 
                handlers = (events || {})[ event.type ];
 
@@ -600,6 +575,12 @@ jQuery.Event = function( src ) {
        if ( src && src.type ) {
                this.originalEvent = src;
                this.type = src.type;
+
+               // Events bubbling up the document may have been marked as prevented
+               // by a handler lower down the tree; reflect the correct value.
+               this.isDefaultPrevented = (src.defaultPrevented || src.returnValue === false ||
+                       src.getPreventDefault && src.getPreventDefault()) ? returnTrue : returnFalse;
+
        // Event type
        } else {
                this.type = src;
@@ -671,13 +652,15 @@ var withinElement = function( event ) {
 
        // Firefox sometimes assigns relatedTarget a XUL element
        // which we cannot access the parentNode property of
+       // Chrome does something similar, the parentNode property
+       // can be accessed but is null.
        try {
                // Traverse up the tree
                while ( parent && parent !== this ) {
                        parent = parent.parentNode;
                }
 
-               if ( parent !== this ) {
+               if ( parent && parent !== this ) {
                        // set the correct event type
                        event.type = event.data;
 
@@ -781,12 +764,12 @@ if ( !jQuery.support.changeBubbles ) {
                        return;
                }
 
-               data = jQuery.data( elem, "_change_data" );
+               data = jQuery._data( elem, "_change_data" );
                val = getVal(elem);
 
                // the current data will be also retrieved by beforeactivate
                if ( e.type !== "focusout" || elem.type !== "radio" ) {
-                       jQuery.data( elem, "_change_data", val );
+                       jQuery._data( elem, "_change_data", val );
                }
 
                if ( data === undefined || val === data ) {
@@ -831,7 +814,7 @@ if ( !jQuery.support.changeBubbles ) {
                        // information
                        beforeactivate: function( e ) {
                                var elem = e.target;
-                               jQuery.data( elem, "_change_data", getVal(elem) );
+                               jQuery._data( elem, "_change_data", getVal(elem) );
                        }
                },
 
@@ -870,21 +853,17 @@ if ( document.addEventListener ) {
        jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
                jQuery.event.special[ fix ] = {
                        setup: function() {
-                               if ( focusCounts[fix]++ === 0 ) {
-                                       document.addEventListener( orig, handler, true );
-                               }
+                               this.addEventListener( orig, handler, true );
                        },
                        teardown: function() {
-                               if ( --focusCounts[fix] === 0 ) {
-                                       document.removeEventListener( orig, handler, true );
-                               }
+                               this.removeEventListener( orig, handler, true );
                        }
                };
 
                function handler( e ) {
                        e = jQuery.event.fix( e );
                        e.type = fix;
-                       return jQuery.event.trigger( e, null, e.target );
+                       return jQuery.event.handle.call( this, e );
                }
        });
 }
@@ -980,8 +959,8 @@ jQuery.fn.extend({
 
                return this.click( jQuery.proxy( fn, function( event ) {
                        // Figure out which function to execute
-                       var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i;
-                       jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 );
+                       var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i;
+                       jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 );
 
                        // Make sure that clicks stop
                        event.preventDefault();
@@ -1069,11 +1048,7 @@ function liveHandler( event ) {
        var stop, maxLevel, related, match, handleObj, elem, j, i, l, data, close, namespace, ret,
                elems = [],
                selectors = [],
-               events = jQuery.data( this, this.nodeType ? "events" : "__events__" );
-
-       if ( typeof events === "function" ) {
-               events = events.events;
-       }
+               events = jQuery._data( this, "events" );
 
        // Make sure we avoid non-left-click bubbling in Firefox (#3861) and disabled elements in IE (#6911)
        if ( event.liveFired === this || !events || !events.live || event.target.disabled || event.button && event.type === "click" ) {
@@ -1177,21 +1152,4 @@ jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblcl
        }
 });
 
-// Prevent memory leaks in IE
-// Window isn't included so as not to unbind existing unload events
-// More info:
-//  - http://isaacschlueter.com/2006/10/msie-memory-leaks/
-if ( window.attachEvent && !window.addEventListener ) {
-       jQuery(window).bind("unload", function() {
-               for ( var id in jQuery.cache ) {
-                       if ( jQuery.cache[ id ].handle ) {
-                               // Try/Catch is to handle iframes being unloaded, see #4280
-                               try {
-                                       jQuery.event.remove( jQuery.cache[ id ].handle.elem );
-                               } catch(e) {}
-                       }
-               }
-       });
-}
-
 })( jQuery );