Make sure that mousing over Chrome "internal div" elements results in no trigger...
[jquery.git] / src / event.js
index d949d0f..a17cf89 100644 (file)
@@ -31,6 +31,9 @@ jQuery.event = {
 
                if ( handler === false ) {
                        handler = returnFalse;
+               } else if ( !handler ) {
+                       // Fixes bug #7229. Fix recommended by jdalton
+                 return;
                }
 
                var handleObjIn, handleObj;
@@ -46,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
@@ -56,21 +59,8 @@ jQuery.event = {
 
                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.events = elemData = function(){};
-                       }
 
+               if ( !events ) {
                        elemData.events = events = {};
                }
 
@@ -136,9 +126,9 @@ jQuery.event = {
                                        }
                                }
                        }
-                       
-                       if ( special.add ) { 
-                               special.add.call( elem, handleObj ); 
+
+                       if ( special.add ) {
+                               special.add.call( elem, handleObj );
 
                                if ( !handleObj.handler.guid ) {
                                        handleObj.handler.guid = handler.guid;
@@ -170,17 +160,12 @@ jQuery.event = {
                }
 
                var ret, type, fn, j, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType,
-                       elemData = jQuery.data( elem ),
+                       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 ) {
@@ -214,7 +199,7 @@ jQuery.event = {
                                namespaces = type.split(".");
                                type = namespaces.shift();
 
-                               namespace = new RegExp("(^|\\.)" + 
+                               namespace = new RegExp("(^|\\.)" +
                                        jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)");
                        }
 
@@ -281,11 +266,8 @@ jQuery.event = {
                        delete elemData.events;
                        delete elemData.handle;
 
-                       if ( typeof elemData === "function" ) {
-                               jQuery.removeData( elem, "events" );
-
-                       } else if ( jQuery.isEmptyObject( elemData ) ) {
-                               jQuery.removeData( elem );
+                       if ( jQuery.isEmptyObject( elemData ) ) {
+                               jQuery.removeData( elem, undefined, true );
                        }
                }
        },
@@ -317,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 );
                                                }
                                        });
                                }
@@ -344,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 );
@@ -370,11 +357,13 @@ jQuery.event = {
                        jQuery.event.trigger( event, data, parent, true );
 
                } else if ( !event.isDefaultPrevented() ) {
-                       var target = event.target, old, targetType = type.replace(rnamespaces, ""),
-                               isClick = jQuery.nodeName(target, "a") && targetType === "click",
+                       var old,
+                               target = event.target,
+                               targetType = type.replace( rnamespaces, "" ),
+                               isClick = jQuery.nodeName( target, "a" ) && targetType === "click",
                                special = jQuery.event.special[ targetType ] || {};
 
-                       if ( (!special._default || special._default.call( elem, event ) === false) && 
+                       if ( (!special._default || special._default.call( elem, event ) === false) &&
                                !isClick && !(target && target.nodeName && jQuery.noData[target.nodeName.toLowerCase()]) ) {
 
                                try {
@@ -403,7 +392,9 @@ jQuery.event = {
        },
 
        handle: function( event ) {
-               var all, handlers, namespaces, namespace_sort = [], namespace_re, events, args = jQuery.makeArray( arguments );
+               var all, handlers, namespaces, namespace_re, events,
+                       namespace_sort = [],
+                       args = jQuery.makeArray( arguments );
 
                event = args[0] = jQuery.event.fix( event || window.event );
                event.currentTarget = this;
@@ -420,11 +411,7 @@ jQuery.event = {
 
                event.namespace = event.namespace || namespace_sort.join(".");
 
-               events = jQuery.data(this, "events");
-
-               if ( typeof events === "function" ) {
-                       events = events.events;
-               }
+               events = jQuery._data(this, "events");
 
                handlers = (events || {})[ event.type ];
 
@@ -442,7 +429,7 @@ jQuery.event = {
                                        event.handler = handleObj.handler;
                                        event.data = handleObj.data;
                                        event.handleObj = handleObj;
-       
+
                                        var ret = handleObj.handler.apply( this, args );
 
                                        if ( ret !== undefined ) {
@@ -482,7 +469,8 @@ jQuery.event = {
 
                // Fix target property, if necessary
                if ( !event.target ) {
-                       event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
+                       // Fixes #1925 where srcElement might not be defined either
+                       event.target = event.srcElement || document;
                }
 
                // check if target is a textnode (safari)
@@ -497,7 +485,9 @@ jQuery.event = {
 
                // Calculate pageX/Y if missing and clientX/Y available
                if ( event.pageX == null && event.clientX != null ) {
-                       var doc = document.documentElement, body = document.body;
+                       var doc = document.documentElement,
+                               body = document.body;
+
                        event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
                        event.pageY = event.clientY + (doc && doc.scrollTop  || body && body.scrollTop  || 0) - (doc && doc.clientTop  || body && body.clientTop  || 0);
                }
@@ -538,7 +528,7 @@ jQuery.event = {
                        add: function( handleObj ) {
                                jQuery.event.add( this,
                                        liveConvert( handleObj.origType, handleObj.selector ),
-                                       jQuery.extend({}, handleObj, {handler: liveHandler, guid: handleObj.handler.guid}) ); 
+                                       jQuery.extend({}, handleObj, {handler: liveHandler, guid: handleObj.handler.guid}) );
                        },
 
                        remove: function( handleObj ) {
@@ -568,7 +558,7 @@ jQuery.removeEvent = document.removeEventListener ?
                if ( elem.removeEventListener ) {
                        elem.removeEventListener( type, handle, false );
                }
-       } : 
+       } :
        function( elem, type, handle ) {
                if ( elem.detachEvent ) {
                        elem.detachEvent( "on" + type, handle );
@@ -585,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;
@@ -615,7 +611,7 @@ jQuery.Event.prototype = {
                if ( !e ) {
                        return;
                }
-               
+
                // if preventDefault exists run it on the original event
                if ( e.preventDefault ) {
                        e.preventDefault();
@@ -656,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;
 
@@ -701,18 +699,20 @@ if ( !jQuery.support.submitBubbles ) {
 
        jQuery.event.special.submit = {
                setup: function( data, namespaces ) {
-                       if ( this.nodeName.toLowerCase() !== "form" ) {
+                       if ( this.nodeName && this.nodeName.toLowerCase() !== "form" ) {
                                jQuery.event.add(this, "click.specialSubmit", function( e ) {
-                                       var elem = e.target, type = elem.type;
+                                       var elem = e.target,
+                                               type = elem.type;
 
                                        if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
                                                e.liveFired = undefined;
                                                return trigger( "submit", this, arguments );
                                        }
                                });
-        
+
                                jQuery.event.add(this, "keypress.specialSubmit", function( e ) {
-                                       var elem = e.target, type = elem.type;
+                                       var elem = e.target,
+                                               type = elem.type;
 
                                        if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
                                                e.liveFired = undefined;
@@ -764,14 +764,14 @@ 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 ) {
                        return;
                }
@@ -785,7 +785,9 @@ if ( !jQuery.support.changeBubbles ) {
 
        jQuery.event.special.change = {
                filters: {
-                       focusout: testChange, 
+                       focusout: testChange,
+
+                       beforedeactivate: testChange,
 
                        click: function( e ) {
                                var elem = e.target, type = elem.type;
@@ -812,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) );
                        }
                },
 
@@ -852,13 +854,13 @@ if ( document.addEventListener ) {
                jQuery.event.special[ fix ] = {
                        setup: function() {
                                this.addEventListener( orig, handler, true );
-                       }, 
-                       teardown: function() { 
+                       },
+                       teardown: function() {
                                this.removeEventListener( orig, handler, true );
                        }
                };
 
-               function handler( e ) { 
+               function handler( e ) {
                        e = jQuery.event.fix( e );
                        e.type = fix;
                        return jQuery.event.handle.call( this, e );
@@ -875,7 +877,7 @@ jQuery.each(["bind", "one"], function( i, name ) {
                        }
                        return this;
                }
-               
+
                if ( jQuery.isFunction( data ) || data === false ) {
                        fn = data;
                        data = undefined;
@@ -915,20 +917,20 @@ jQuery.fn.extend({
 
                return this;
        },
-       
+
        delegate: function( selector, types, data, fn ) {
                return this.live( types, data, fn, selector );
        },
-       
+
        undelegate: function( selector, types, fn ) {
                if ( arguments.length === 0 ) {
                                return this.unbind( "live" );
-               
+
                } else {
                        return this.die( types, null, fn, selector );
                }
        },
-       
+
        trigger: function( type, data ) {
                return this.each(function() {
                        jQuery.event.trigger( type, data, this );
@@ -947,7 +949,8 @@ jQuery.fn.extend({
 
        toggle: function( fn ) {
                // Save reference to arguments for access in closure
-               var args = arguments, i = 1;
+               var args = arguments,
+                       i = 1;
 
                // link all the functions, so any of them can unbind this click handler
                while ( i < args.length ) {
@@ -956,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();
@@ -984,12 +987,12 @@ jQuery.each(["live", "die"], function( i, name ) {
                var type, i = 0, match, namespaces, preType,
                        selector = origSelector || this.selector,
                        context = origSelector ? this : jQuery( this.context );
-               
+
                if ( typeof types === "object" && !types.preventDefault ) {
                        for ( var key in types ) {
                                context[ name ]( key, data, types[key], selector );
                        }
-                       
+
                        return this;
                }
 
@@ -1036,22 +1039,19 @@ jQuery.each(["live", "die"], function( i, name ) {
                                context.unbind( "live." + liveConvert( type, selector ), fn );
                        }
                }
-               
+
                return this;
        };
 });
 
 function liveHandler( event ) {
-       var stop, maxLevel, elems = [], selectors = [],
-               related, match, handleObj, elem, j, i, l, data, close, namespace, ret,
-               events = jQuery.data( this, "events" );
-
-       if ( typeof events === "function" ) {
-               events = events.events;
-       }
+       var stop, maxLevel, related, match, handleObj, elem, j, i, l, data, close, namespace, ret,
+               elems = [],
+               selectors = [],
+               events = jQuery._data( this, "events" );
 
-       // Make sure we avoid non-left-click bubbling in Firefox (#3861)
-       if ( event.liveFired === this || !events || !events.live || event.button && event.type === "click" ) {
+       // 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" ) {
                return;
        }
 
@@ -1118,6 +1118,9 @@ function liveHandler( event ) {
                        if ( ret === false ) {
                                stop = false;
                        }
+                       if ( event.isImmediatePropagationStopped() ) {
+                               break;
+                       }
                }
        }
 
@@ -1149,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 );