fix for #3533, triggering an event with a colon in the name on a table no longer...
[jquery.git] / src / event.js
index 5d701ee..9c1f287 100644 (file)
@@ -190,9 +190,10 @@ jQuery.event = {
        },
 
        // bubbling is internal
-       trigger: function( event, data, elem, bubbling ) {
+       trigger: function( event, data, elem /*, bubbling */ ) {
                // Event object or event type
-               var type = event.type || event;
+               var type = event.type || event,
+                       bubbling = arguments[3];
 
                if ( !bubbling ) {
                        event = typeof event === "object" ?
@@ -214,11 +215,11 @@ jQuery.event = {
                                event.stopPropagation();
                                // Only trigger if we've ever bound an event for it
                                if ( this.global[ type ] ) {
-                                       for ( var cached in jQuery.cache ) {
-                                               if ( cached.events && cached.events[ type ] ) {
-                                                       this.trigger( event, data, cached.handle.elem );
+                                       jQuery.each( jQuery.cache, function() {
+                                               if ( this.events && this.events[type] ) {
+                                                       jQuery.event.trigger( event, data, this.handle.elem );
                                                }
-                                       }
+                                       });
                                }
                        }
 
@@ -246,16 +247,22 @@ jQuery.event = {
                        handle.apply( elem, data );
                }
 
+               var nativeFn, nativeHandler;
+               try {
+                       nativeFn = elem[ type ];
+                       nativeHandler = elem[ "on" + type ];
+               // prevent IE from throwing an error for some elements with some event types, see #3533
+               } catch (e) {}
                // Handle triggering native .onfoo handlers (and on links since we don't call .click() for links)
-               if ( (!elem[ type ] || (jQuery.nodeName(elem, 'a') && type === "click")) && elem["on"+type] && elem["on"+type].apply( elem, data ) === false ) {
+               if ( (!nativeFn || (jQuery.nodeName(elem, 'a') && type === "click")) && nativeHandler && nativeHandler.apply( elem, data ) === false ) {
                        event.result = false;
                }
 
                // Trigger the native events (except for clicks on links)
-               if ( !bubbling && elem[ type ] && !event.isDefaultPrevented() && !(jQuery.nodeName(elem, 'a') && type === "click") ) {
+               if ( !bubbling && nativeFn && !event.isDefaultPrevented() && !(jQuery.nodeName(elem, 'a') && type === "click") ) {
                        this.triggered = true;
                        try {
-                               elem[ type ]();
+                               nativeFn();
                        // prevent IE from throwing an error for some hidden elements
                        } catch (e) {}
                }
@@ -367,15 +374,20 @@ jQuery.event = {
 
                // Add which for click: 1 == left; 2 == middle; 3 == right
                // Note: button is not normalized, so don't use it
-               if ( !event.which && event.button ) {
+               if ( !event.which && event.button !== undefined ) {
                        event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
                }
 
                return event;
        },
 
-       proxy: function( fn, proxy ) {
-               proxy = proxy || function() { return fn.apply( this, arguments ); };
+       proxy: function( fn, proxy, thisObject ) {
+               if ( proxy !== undefined && !jQuery.isFunction( proxy ) ) {
+                       thisObject = proxy;
+                       proxy = undefined;
+               }
+               // FIXME: Should proxy be redefined to be applied with thisObject if defined?
+               proxy = proxy || function() { return fn.apply( thisObject !== undefined ? thisObject : this, arguments ); };
                // Set the guid of unique handler to the same of original handler, so it can be removed
                proxy.guid = fn.guid = fn.guid || proxy.guid || this.guid++;
                // So proxy can be declared as an argument
@@ -388,16 +400,28 @@ jQuery.event = {
                        setup: bindReady,
                        teardown: function() {}
                },
-               
+
                live: {
                        add: function( proxy, data, namespaces ) {
                                jQuery.extend( proxy, data || {} );
                                proxy.guid += data.selector + data.live;
                                jQuery.event.add( this, data.live, liveHandler );
                        },
-                       
-                       teardown: function( namespaces ) {
-                               jQuery.event.remove( this, namespaces[0], liveHandler );
+
+                       remove: function( namespaces ) {
+                               if ( namespaces.length ) {
+                                       var remove = 0, name = new RegExp("(^|\\.)" + namespaces[0] + "(\\.|$)");
+
+                                       jQuery.each( (jQuery.data(this, "events").live || {}), function() {
+                                               if ( name.test(this.type) ) {
+                                                       remove++;
+                                               }
+                                       });
+
+                                       if ( remove < 1 ) {
+                                               jQuery.event.remove( this, namespaces[0], liveHandler );
+                                       }
+                               }
                        }
                }
        }
@@ -479,8 +503,11 @@ var withinElement = function( event ) {
        var parent = event.relatedTarget;
        // Traverse up the tree
        while ( parent && parent != this ) {
+               // Firefox sometimes assigns relatedTarget a XUL element
+               // which we cannot access the parentNode property of
                try { parent = parent.parentNode; }
-               catch(e) { parent = this; }
+               // assuming we've left the element since we most likely mousedover a xul element
+               catch(e) { break; }
        }
 
        if ( parent != this ) {
@@ -506,19 +533,35 @@ jQuery.each({
 });
 
 jQuery.fn.extend({
-       bind: function( type, data, fn ) {
-               return type === "unload" ? this.one(type, data, fn) : this.each(function() {
-                       jQuery.event.add( this, type, fn || data, fn && data );
+       bind: function( type, data, fn, thisObject ) {
+               if ( jQuery.isFunction( data ) ) {
+                       if ( fn !== undefined ) {
+                               thisObject = fn;
+                       }
+                       fn = data;
+                       data = undefined;
+               }
+               fn = thisObject === undefined ? fn : jQuery.event.proxy( fn, thisObject );
+               return type === "unload" ? this.one(type, data, fn, thisObject) : this.each(function() {
+                       jQuery.event.add( this, type, fn, data );
                });
        },
 
-       one: function( type, data, fn ) {
-               var one = jQuery.event.proxy( fn || data, function( event ) {
+       one: function( type, data, fn, thisObject ) {
+               if ( jQuery.isFunction( data ) ) {
+                       if ( fn !== undefined ) {
+                               thisObject = fn;
+                       }
+                       fn = data;
+                       data = undefined;
+               }
+               fn = thisObject === undefined ? fn : jQuery.event.proxy( fn, thisObject );
+               var one = jQuery.event.proxy( fn, function( event ) {
                        jQuery( this ).unbind( event, one );
-                       return (fn || data).apply( this, arguments );
+                       return fn.apply( this, arguments );
                });
                return this.each(function() {
-                       jQuery.event.add( this, type, one, fn && data );
+                       jQuery.event.add( this, type, one, data );
                });
        },
 
@@ -566,7 +609,7 @@ jQuery.fn.extend({
        },
 
        hover: function( fnOver, fnOut ) {
-               return this.mouseenter( fnOver ).mouseleave( fnOut );
+               return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
        },
 
        ready: function( fn ) {
@@ -587,10 +630,17 @@ jQuery.fn.extend({
                return this;
        },
 
-       live: function( type, data, fn ) {
+       live: function( type, data, fn, thisObject ) {
+               if ( jQuery.isFunction( data ) ) {
+                       if ( fn !== undefined ) {
+                               thisObject = fn;
+                       }
+                       fn = data;
+                       data = undefined;
+               }
                jQuery( this.context ).bind( liveConvert( type, this.selector ), {
-                       data: fn && data, selector: this.selector, live: type
-               }, fn || data );
+                       data: data, selector: this.selector, live: type
+               }, fn, thisObject );
                return this;
        },
 
@@ -618,7 +668,7 @@ function liveHandler( event ) {
 
        jQuery.each(elems, function() {
                event.currentTarget = this.elem;
-               event.data = this.fn.data
+               event.data = this.fn.data;
                if ( this.fn.apply( this.elem, args ) === false ) {
                        return (stop = false);
                }
@@ -715,7 +765,7 @@ jQuery.each( ("blur,focus,load,resize,scroll,unload,click,dblclick," +
 
        // Handle event binding
        jQuery.fn[ name ] = function( fn ) {
-               return fn ? this.bind (name, fn ) : this.trigger( name );
+               return fn ? this.bind( name, fn ) : this.trigger( name );
        };
 });