Make sure that live focus and blur events also bind to the regular events, to handle...
[jquery.git] / src / event.js
index f7c69f8..d59533f 100644 (file)
@@ -99,7 +99,7 @@ jQuery.event = {
                                // Check for a special event handler
                                // Only use addEventListener/attachEvent if the special
                                // events handler returns false
-                               if ( !special.setup || special.setup.call( elem, data, namespaces ) === false ) {
+                               if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
                                        // Bind the global event handler to the element
                                        if ( elem.addEventListener ) {
                                                elem.addEventListener( type, eventHandle, false );
@@ -221,7 +221,7 @@ jQuery.event = {
                        }
 
                        // remove generic event handler if no more handlers exist
-                       if ( jQuery.isEmptyObject( events[ type ] ) ) {
+                       if ( eventType.length === 0 || pos != null && eventType.length === 1 ) {
                                if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
                                        removeEvent( elem, type, elemData.handle );
                                }
@@ -362,7 +362,7 @@ jQuery.event = {
                event.currentTarget = this;
 
                // Namespaced event handlers
-               all = event.type.indexOf(".") < 0;
+               all = event.type.indexOf(".") < 0 && !event.exclusive;
 
                if ( !all ) {
                        namespaces = event.type.split(".");
@@ -380,7 +380,7 @@ jQuery.event = {
                                var handleObj = handlers[ j ];
 
                                // Filter the functions by class
-                               if ( (all && !event.exclusive) || namespace.test( handleObj.namespace ) ) {
+                               if ( all || namespace.test( handleObj.namespace ) ) {
                                        // Pass in a reference to the handler function itself
                                        // So that we can later remove it
                                        event.handler = handleObj.handler;
@@ -502,16 +502,16 @@ jQuery.event = {
                },
 
                beforeunload: {
-                       setup: function( data, namespaces, fn ) {
+                       setup: function( data, namespaces, eventHandle ) {
                                // We only want to do this special case on windows
                                if ( this.setInterval ) {
-                                       this.onbeforeunload = fn;
+                                       this.onbeforeunload = eventHandle;
                                }
 
                                return false;
                        },
-                       teardown: function( namespaces, fn ) {
-                               if ( this.onbeforeunload === fn ) {
+                       teardown: function( namespaces, eventHandle ) {
+                               if ( this.onbeforeunload === eventHandle ) {
                                        this.onbeforeunload = null;
                                }
                        }
@@ -652,64 +652,65 @@ jQuery.each({
 // submit delegation
 if ( !jQuery.support.submitBubbles ) {
 
-jQuery.event.special.submit = {
-       setup: function( data, namespaces, fn ) {
-               if ( this.nodeName.toLowerCase() !== "form" ) {
-                       jQuery.event.add(this, "click.specialSubmit." + fn.guid, function( e ) {
-                               var elem = e.target, type = elem.type;
+       jQuery.event.special.submit = {
+               setup: function( data, namespaces ) {
+                       if ( this.nodeName.toLowerCase() !== "form" ) {
+                               jQuery.event.add(this, "click.specialSubmit", function( e ) {
+                                       var elem = e.target, type = elem.type;
 
-                               if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
-                                       return trigger( "submit", this, arguments );
-                               }
-                       });
+                                       if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
+                                               return trigger( "submit", this, arguments );
+                                       }
+                               });
         
-                       jQuery.event.add(this, "keypress.specialSubmit." + fn.guid, function( e ) {
-                               var elem = e.target, type = elem.type;
+                               jQuery.event.add(this, "keypress.specialSubmit", function( e ) {
+                                       var elem = e.target, type = elem.type;
 
-                               if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
-                                       return trigger( "submit", this, arguments );
-                               }
-                       });
+                                       if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
+                                               return trigger( "submit", this, arguments );
+                                       }
+                               });
 
-               } else {
-                       return false;
-               }
-       },
+                       } else {
+                               return false;
+                       }
+               },
 
-       remove: function( namespaces, fn ) {
-               jQuery.event.remove( this, "click.specialSubmit" + (fn ? "."+fn.guid : "") );
-               jQuery.event.remove( this, "keypress.specialSubmit" + (fn ? "."+fn.guid : "") );
-       }
-};
+               teardown: function( namespaces ) {
+                       jQuery.event.remove( this, ".specialSubmit" );
+               }
+       };
 
 }
 
 // change delegation, happens here so we have bind.
 if ( !jQuery.support.changeBubbles ) {
 
-var formElems = /textarea|input|select/i;
+       var formElems = /textarea|input|select/i,
 
-function getVal( elem ) {
-       var type = elem.type, val = elem.value;
+       changeFilters,
 
-       if ( type === "radio" || type === "checkbox" ) {
-               val = elem.checked;
+       getVal = function( elem ) {
+               var type = elem.type, val = elem.value;
 
-       } else if ( type === "select-multiple" ) {
-               val = elem.selectedIndex > -1 ?
-                       jQuery.map( elem.options, function( elem ) {
-                               return elem.selected;
-                       }).join("-") :
-                       "";
+               if ( type === "radio" || type === "checkbox" ) {
+                       val = elem.checked;
 
-       } else if ( elem.nodeName.toLowerCase() === "select" ) {
-               val = elem.selectedIndex;
-       }
+               } else if ( type === "select-multiple" ) {
+                       val = elem.selectedIndex > -1 ?
+                               jQuery.map( elem.options, function( elem ) {
+                                       return elem.selected;
+                               }).join("-") :
+                               "";
 
-       return val;
-}
+               } else if ( elem.nodeName.toLowerCase() === "select" ) {
+                       val = elem.selectedIndex;
+               }
 
-function testChange( e ) {
+               return val;
+       },
+
+       testChange = function testChange( e ) {
                var elem = e.target, data, val;
 
                if ( !formElems.test( elem.nodeName ) || elem.readOnly ) {
@@ -732,58 +733,61 @@ function testChange( e ) {
                        e.type = "change";
                        return jQuery.event.trigger( e, arguments[1], elem );
                }
-}
+       };
 
-jQuery.event.special.change = {
-       filters: {
-               focusout: testChange, 
+       jQuery.event.special.change = {
+               filters: {
+                       focusout: testChange, 
 
-               click: function( e ) {
-                       var elem = e.target, type = elem.type;
+                       click: function( e ) {
+                               var elem = e.target, type = elem.type;
 
-                       if ( type === "radio" || type === "checkbox" || elem.nodeName.toLowerCase() === "select" ) {
-                               return testChange.call( this, e );
+                               if ( type === "radio" || type === "checkbox" || elem.nodeName.toLowerCase() === "select" ) {
+                                       return testChange.call( this, e );
+                               }
+                       },
+
+                       // Change has to be called before submit
+                       // Keydown will be called before keypress, which is used in submit-event delegation
+                       keydown: function( e ) {
+                               var elem = e.target, type = elem.type;
+
+                               if ( (e.keyCode === 13 && elem.nodeName.toLowerCase() !== "textarea") ||
+                                       (e.keyCode === 32 && (type === "checkbox" || type === "radio")) ||
+                                       type === "select-multiple" ) {
+                                       return testChange.call( this, e );
+                               }
+                       },
+
+                       // Beforeactivate happens also before the previous element is blurred
+                       // with this event you can't trigger a change event, but you can store
+                       // information/focus[in] is not needed anymore
+                       beforeactivate: function( e ) {
+                               var elem = e.target;
+                               jQuery.data( elem, "_change_data", getVal(elem) );
                        }
                },
 
-               // Change has to be called before submit
-               // Keydown will be called before keypress, which is used in submit-event delegation
-               keydown: function( e ) {
-                       var elem = e.target, type = elem.type;
+               setup: function( data, namespaces ) {
+                       if ( this.type === "file" ) {
+                               return false;
+                       }
 
-                       if ( (e.keyCode === 13 && elem.nodeName.toLowerCase() !== "textarea") ||
-                               (e.keyCode === 32 && (type === "checkbox" || type === "radio")) ||
-                               type === "select-multiple" ) {
-                               return testChange.call( this, e );
+                       for ( var type in changeFilters ) {
+                               jQuery.event.add( this, type + ".specialChange", changeFilters[type] );
                        }
+
+                       return formElems.test( this.nodeName );
                },
 
-               // Beforeactivate happens also before the previous element is blurred
-               // with this event you can't trigger a change event, but you can store
-               // information/focus[in] is not needed anymore
-               beforeactivate: function( e ) {
-                       var elem = e.target;
-                       jQuery.data( elem, "_change_data", getVal(elem) );
-               }
-       },
-       setup: function( data, namespaces, fn ) {
-               for ( var type in changeFilters ) {
-                       jQuery.event.add( this, type + ".specialChange." + fn.guid, changeFilters[type] );
-               }
+               teardown: function( namespaces ) {
+                       jQuery.event.remove( this, ".specialChange" );
 
-               return formElems.test( this.nodeName );
-       },
-       remove: function( namespaces, fn ) {
-               for ( var type in changeFilters ) {
-                       jQuery.event.remove( this, type + ".specialChange" + (fn ? "."+fn.guid : ""), changeFilters[type] );
+                       return formElems.test( this.nodeName );
                }
+       };
 
-               return formElems.test( this.nodeName );
-       }
-};
-
-var changeFilters = jQuery.event.special.change.filters;
-
+       changeFilters = jQuery.event.special.change.filters;
 }
 
 function trigger( type, elem, args ) {
@@ -917,9 +921,16 @@ jQuery.fn.extend({
        }
 });
 
+var liveMap = {
+       focus: "focusin",
+       blur: "focusout",
+       mouseenter: "mouseover",
+       mouseleave: "mouseout"
+};
+
 jQuery.each(["live", "die"], function( i, name ) {
        jQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) {
-               var type, i = 0, match, namespaces,
+               var type, i = 0, match, namespaces, preType,
                        selector = origSelector || this.selector,
                        context = origSelector ? this : jQuery( this.context );
 
@@ -939,18 +950,26 @@ jQuery.each(["live", "die"], function( i, name ) {
                                type = type.replace( rnamespaces, "" );
                        }
 
-                       type = type === "focus" ? "focusin" : // focus --> focusin
-                                       type === "blur" ? "focusout" : // blur --> focusout
-                                       type === "hover" ? types.push("mouseleave" + namespaces) && "mouseenter" : // hover support
-                                       type;
+                       if ( type === "hover" ) {
+                               types.push( "mouseenter" + namespaces, "mouseleave" + namespaces );
+                               continue;
+                       }
+
+                       preType = type;
 
-                       type += namespaces;
+                       if ( type === "focus" || type === "blur" ) {
+                               types.push( liveMap[ type ] + namespaces );
+                               type = type + namespaces;
+
+                       } else {
+                               type = (liveMap[ type ] || type) + namespaces;
+                       }
 
                        if ( name === "live" ) {
                                // bind live handler
                                context.each(function(){
                                        jQuery.event.add( this, liveConvert( type, selector ),
-                                               { data: data, selector: selector, handler: fn, origType: type, origHandler: fn } );
+                                               { data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } );
                                });
 
                        } else {
@@ -969,7 +988,7 @@ function liveHandler( event ) {
                events = jQuery.data( this, "events" );
 
        // Make sure we avoid non-left-click bubbling in Firefox (#3861)
-       if ( event.liveFired === this || !events || event.button && event.type === "click" ) {
+       if ( event.liveFired === this || !events || !events.live || event.button && event.type === "click" ) {
                return;
        }
 
@@ -999,7 +1018,7 @@ function liveHandler( event ) {
                                related = null;
 
                                // Those two events require additional checking
-                               if ( handleObj.origType === "mouseenter" || handleObj.origType === "mouseleave" ) {
+                               if ( handleObj.preType === "mouseenter" || handleObj.preType === "mouseleave" ) {
                                        related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0];
                                }