Use a key name less likely to result in collisions for events on plain JS objects...
[jquery.git] / src / event.js
index 588a797..1bbf134 100644 (file)
@@ -1,8 +1,12 @@
+(function( jQuery ) {
+
 var rnamespaces = /\.(.*)$/,
 var rnamespaces = /\.(.*)$/,
+       rformElems = /^(?:textarea|input|select)$/i,
+       rperiod = /\./g,
+       rspace = / /g,
+       rescape = /[^\w\s.|`]/g,
        fcleanup = function( nm ) {
        fcleanup = function( nm ) {
-               return nm.replace(/[^\w\s\.\|`]/g, function( ch ) {
-                       return "\\" + ch;
-               });
+               return nm.replace(rescape, "\\$&");
        };
 
 /*
        };
 
 /*
@@ -21,7 +25,7 @@ jQuery.event = {
 
                // For whatever reason, IE has trouble passing the window object
                // around, causing it to be cloned in the process
 
                // For whatever reason, IE has trouble passing the window object
                // around, causing it to be cloned in the process
-               if ( elem.setInterval && ( elem !== window && !elem.frameElement ) ) {
+               if ( jQuery.isWindow( elem ) && ( elem !== window && !elem.frameElement ) ) {
                        elem = window;
                }
 
                        elem = window;
                }
 
@@ -50,8 +54,28 @@ jQuery.event = {
                        return;
                }
 
                        return;
                }
 
-               var events = elemData.events = elemData.events || {},
+               // 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 ],
                        eventHandle = elemData.handle;
                        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(){};
+                       }
+
+                       elemData.events = events = {};
+               }
 
                if ( !eventHandle ) {
                        elemData.handle = eventHandle = function() {
 
                if ( !eventHandle ) {
                        elemData.handle = eventHandle = function() {
@@ -149,12 +173,18 @@ jQuery.event = {
                }
 
                var ret, type, fn, j, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType,
                }
 
                var ret, type, fn, j, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType,
+                       eventKey = elem.nodeType ? "events" : "__events__",
                        elemData = jQuery.data( elem ),
                        elemData = jQuery.data( elem ),
-                       events = elemData && elemData.events;
+                       events = elemData && elemData[ eventKey ];
 
                if ( !elemData || !events ) {
                        return;
                }
 
                if ( !elemData || !events ) {
                        return;
                }
+               
+               if ( typeof events === "function" ) {
+                       elemData = events;
+                       events = events.events;
+               }
 
                // types is actually an event object here
                if ( types && types.type ) {
 
                // types is actually an event object here
                if ( types && types.type ) {
@@ -237,7 +267,7 @@ jQuery.event = {
                        // remove generic event handler if no more handlers exist
                        if ( eventType.length === 0 || pos != null && eventType.length === 1 ) {
                                if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
                        // remove generic event handler if no more handlers exist
                        if ( eventType.length === 0 || pos != null && eventType.length === 1 ) {
                                if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
-                                       removeEvent( elem, type, elemData.handle );
+                                       jQuery.removeEvent( elem, type, elemData.handle );
                                }
 
                                ret = null;
                                }
 
                                ret = null;
@@ -252,10 +282,13 @@ jQuery.event = {
                                handle.elem = null;
                        }
 
                                handle.elem = null;
                        }
 
-                       delete elemData.events;
+                       delete elemData[ eventKey ];
                        delete elemData.handle;
 
                        delete elemData.handle;
 
-                       if ( jQuery.isEmptyObject( elemData ) ) {
+                       if ( typeof elemData === "function" ) {
+                               jQuery.removeData( elem, "events" );
+
+                       } else if ( jQuery.isEmptyObject( elemData ) ) {
                                jQuery.removeData( elem );
                        }
                }
                                jQuery.removeData( elem );
                        }
                }
@@ -270,7 +303,7 @@ jQuery.event = {
                if ( !bubbling ) {
                        event = typeof event === "object" ?
                                // jQuery.Event object
                if ( !bubbling ) {
                        event = typeof event === "object" ?
                                // jQuery.Event object
-                               event[expando] ? event :
+                               event[ jQuery.expando ] ? event :
                                // Object literal
                                jQuery.extend( jQuery.Event(type), event ) :
                                // Just the event type (string)
                                // Object literal
                                jQuery.extend( jQuery.Event(type), event ) :
                                // Just the event type (string)
@@ -315,7 +348,10 @@ jQuery.event = {
                event.currentTarget = elem;
 
                // Trigger the event, it is assumed that "handle" is a function
                event.currentTarget = elem;
 
                // Trigger the event, it is assumed that "handle" is a function
-               var handle = jQuery.data( elem, "handle" );
+               var handle = elem.nodeType ?
+                       jQuery.data( elem, "handle" ) :
+                       (jQuery.data( elem, "__events__" ) || {}).handle;
+
                if ( handle ) {
                        handle.apply( elem, data );
                }
                if ( handle ) {
                        handle.apply( elem, data );
                }
@@ -327,6 +363,7 @@ jQuery.event = {
                        if ( !(elem && elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()]) ) {
                                if ( elem[ "on" + type ] && elem[ "on" + type ].apply( elem, data ) === false ) {
                                        event.result = false;
                        if ( !(elem && elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()]) ) {
                                if ( elem[ "on" + type ] && elem[ "on" + type ].apply( elem, data ) === false ) {
                                        event.result = false;
+                                       event.preventDefault();
                                }
                        }
 
                                }
                        }
 
@@ -337,7 +374,7 @@ jQuery.event = {
                        jQuery.event.trigger( event, data, parent, true );
 
                } else if ( !event.isDefaultPrevented() ) {
                        jQuery.event.trigger( event, data, parent, true );
 
                } else if ( !event.isDefaultPrevented() ) {
-                       var target = event.target, old, targetType = type.replace(/\..*$/, ""),
+                       var target = event.target, old, targetType = type.replace(rnamespaces, ""),
                                isClick = jQuery.nodeName(target, "a") && targetType === "click",
                                special = jQuery.event.special[ targetType ] || {};
 
                                isClick = jQuery.nodeName(target, "a") && targetType === "click",
                                special = jQuery.event.special[ targetType ] || {};
 
@@ -387,7 +424,12 @@ jQuery.event = {
 
                event.namespace = event.namespace || namespace_sort.join(".");
 
 
                event.namespace = event.namespace || namespace_sort.join(".");
 
-               events = jQuery.data(this, "events");
+               events = jQuery.data(this, this.nodeType ? "events" : "__events__");
+
+               if ( typeof events === "function" ) {
+                       events = events.events;
+               }
+
                handlers = (events || {})[ event.type ];
 
                if ( events && handlers ) {
                handlers = (events || {})[ event.type ];
 
                if ( events && handlers ) {
@@ -425,10 +467,10 @@ jQuery.event = {
                return event.result;
        },
 
                return event.result;
        },
 
-       props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
+       props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
 
        fix: function( event ) {
 
        fix: function( event ) {
-               if ( event[ expando ] ) {
+               if ( event[ jQuery.expando ] ) {
                        return event;
                }
 
                        return event;
                }
 
@@ -465,8 +507,8 @@ jQuery.event = {
                }
 
                // Add which for key events
                }
 
                // Add which for key events
-               if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) ) {
-                       event.which = event.charCode || event.keyCode;
+               if ( event.which == null && (event.charCode != null || event.keyCode != null) ) {
+                       event.which = event.charCode != null ? event.charCode : event.keyCode;
                }
 
                // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
                }
 
                // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
@@ -511,7 +553,7 @@ jQuery.event = {
                beforeunload: {
                        setup: function( data, namespaces, eventHandle ) {
                                // We only want to do this special case on windows
                beforeunload: {
                        setup: function( data, namespaces, eventHandle ) {
                                // We only want to do this special case on windows
-                               if ( this.setInterval ) {
+                               if ( jQuery.isWindow( this ) ) {
                                        this.onbeforeunload = eventHandle;
                                }
                        },
                                        this.onbeforeunload = eventHandle;
                                }
                        },
@@ -525,7 +567,7 @@ jQuery.event = {
        }
 };
 
        }
 };
 
-var removeEvent = document.removeEventListener ?
+jQuery.removeEvent = document.removeEventListener ?
        function( elem, type, handle ) {
                if ( elem.removeEventListener ) {
                        elem.removeEventListener( type, handle, false );
        function( elem, type, handle ) {
                if ( elem.removeEventListener ) {
                        elem.removeEventListener( type, handle, false );
@@ -557,7 +599,7 @@ jQuery.Event = function( src ) {
        this.timeStamp = jQuery.now();
 
        // Mark it as fixed
        this.timeStamp = jQuery.now();
 
        // Mark it as fixed
-       this[ expando ] = true;
+       this[ jQuery.expando ] = true;
 };
 
 function returnFalse() {
 };
 
 function returnFalse() {
@@ -581,9 +623,11 @@ jQuery.Event.prototype = {
                // if preventDefault exists run it on the original event
                if ( e.preventDefault ) {
                        e.preventDefault();
                // if preventDefault exists run it on the original event
                if ( e.preventDefault ) {
                        e.preventDefault();
-               }
+
                // otherwise set the returnValue property of the original event to false (IE)
                // otherwise set the returnValue property of the original event to false (IE)
-               e.returnValue = false;
+               } else {
+                       e.returnValue = false;
+               }
        },
        stopPropagation: function() {
                this.isPropagationStopped = returnTrue;
        },
        stopPropagation: function() {
                this.isPropagationStopped = returnTrue;
@@ -666,6 +710,7 @@ if ( !jQuery.support.submitBubbles ) {
                                        var elem = e.target, type = elem.type;
 
                                        if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
                                        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 );
                                        }
                                });
                                                return trigger( "submit", this, arguments );
                                        }
                                });
@@ -674,6 +719,7 @@ if ( !jQuery.support.submitBubbles ) {
                                        var elem = e.target, type = elem.type;
 
                                        if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
                                        var elem = e.target, type = elem.type;
 
                                        if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
+                                               e.liveFired = undefined;
                                                return trigger( "submit", this, arguments );
                                        }
                                });
                                                return trigger( "submit", this, arguments );
                                        }
                                });
@@ -693,9 +739,7 @@ if ( !jQuery.support.submitBubbles ) {
 // change delegation, happens here so we have bind.
 if ( !jQuery.support.changeBubbles ) {
 
 // change delegation, happens here so we have bind.
 if ( !jQuery.support.changeBubbles ) {
 
-       var formElems = /textarea|input|select/i,
-
-       changeFilters,
+       var changeFilters,
 
        getVal = function( elem ) {
                var type = elem.type, val = elem.value;
 
        getVal = function( elem ) {
                var type = elem.type, val = elem.value;
@@ -720,7 +764,7 @@ if ( !jQuery.support.changeBubbles ) {
        testChange = function testChange( e ) {
                var elem = e.target, data, val;
 
        testChange = function testChange( e ) {
                var elem = e.target, data, val;
 
-               if ( !formElems.test( elem.nodeName ) || elem.readOnly ) {
+               if ( !rformElems.test( elem.nodeName ) || elem.readOnly ) {
                        return;
                }
 
                        return;
                }
 
@@ -738,6 +782,7 @@ if ( !jQuery.support.changeBubbles ) {
 
                if ( data != null || val ) {
                        e.type = "change";
 
                if ( data != null || val ) {
                        e.type = "change";
+                       e.liveFired = undefined;
                        return jQuery.event.trigger( e, arguments[1], elem );
                }
        };
                        return jQuery.event.trigger( e, arguments[1], elem );
                }
        };
@@ -768,7 +813,7 @@ if ( !jQuery.support.changeBubbles ) {
 
                        // Beforeactivate happens also before the previous element is blurred
                        // with this event you can't trigger a change event, but you can store
 
                        // 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
+                       // information
                        beforeactivate: function( e ) {
                                var elem = e.target;
                                jQuery.data( elem, "_change_data", getVal(elem) );
                        beforeactivate: function( e ) {
                                var elem = e.target;
                                jQuery.data( elem, "_change_data", getVal(elem) );
@@ -784,17 +829,20 @@ if ( !jQuery.support.changeBubbles ) {
                                jQuery.event.add( this, type + ".specialChange", changeFilters[type] );
                        }
 
                                jQuery.event.add( this, type + ".specialChange", changeFilters[type] );
                        }
 
-                       return formElems.test( this.nodeName );
+                       return rformElems.test( this.nodeName );
                },
 
                teardown: function( namespaces ) {
                        jQuery.event.remove( this, ".specialChange" );
 
                },
 
                teardown: function( namespaces ) {
                        jQuery.event.remove( this, ".specialChange" );
 
-                       return formElems.test( this.nodeName );
+                       return rformElems.test( this.nodeName );
                }
        };
 
        changeFilters = jQuery.event.special.change.filters;
                }
        };
 
        changeFilters = jQuery.event.special.change.filters;
+
+       // Handle when the input is .focus()'d
+       changeFilters.focus = changeFilters.beforeactivate;
 }
 
 function trigger( type, elem, args ) {
 }
 
 function trigger( type, elem, args ) {
@@ -940,6 +988,14 @@ jQuery.each(["live", "die"], function( i, name ) {
                var type, i = 0, match, namespaces, preType,
                        selector = origSelector || this.selector,
                        context = origSelector ? this : jQuery( this.context );
                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;
+               }
 
                if ( jQuery.isFunction( data ) ) {
                        fn = data;
 
                if ( jQuery.isFunction( data ) ) {
                        fn = data;
@@ -991,8 +1047,12 @@ jQuery.each(["live", "die"], function( i, name ) {
 
 function liveHandler( event ) {
        var stop, maxLevel, elems = [], selectors = [],
 
 function liveHandler( event ) {
        var stop, maxLevel, elems = [], selectors = [],
-               related, match, handleObj, elem, j, i, l, data, close, namespace,
-               events = jQuery.data( this, "events" );
+               related, match, handleObj, elem, j, i, l, data, close, namespace, ret,
+               events = jQuery.data( this, this.nodeType ? "events" : "__events__" );
+
+       if ( typeof events === "function" ) {
+               events = events.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)
        if ( event.liveFired === this || !events || !events.live || event.button && event.type === "click" ) {
@@ -1069,7 +1129,7 @@ function liveHandler( event ) {
 }
 
 function liveConvert( type, selector ) {
 }
 
 function liveConvert( type, selector ) {
-       return (type && type !== "*" ? type + "." : "") + selector.replace(/\./g, "`").replace(/ /g, "&");
+       return (type && type !== "*" ? type + "." : "") + selector.replace(rperiod, "`").replace(rspace, "&");
 }
 
 jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
 }
 
 jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
@@ -1098,7 +1158,7 @@ jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblcl
 // More info:
 //  - http://isaacschlueter.com/2006/10/msie-memory-leaks/
 if ( window.attachEvent && !window.addEventListener ) {
 // More info:
 //  - http://isaacschlueter.com/2006/10/msie-memory-leaks/
 if ( window.attachEvent && !window.addEventListener ) {
-       window.attachEvent("onunload", function() {
+       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
                for ( var id in jQuery.cache ) {
                        if ( jQuery.cache[ id ].handle ) {
                                // Try/Catch is to handle iframes being unloaded, see #4280
@@ -1109,3 +1169,5 @@ if ( window.attachEvent && !window.addEventListener ) {
                }
        });
 }
                }
        });
 }
+
+})( jQuery );