Made it so that you no longer need to build jQuery in order to run the test suite...
[jquery.git] / src / event.js
index 018d4d9..588a797 100644 (file)
@@ -1,3 +1,10 @@
+var rnamespaces = /\.(.*)$/,
+       fcleanup = function( nm ) {
+               return nm.replace(/[^\w\s\.\|`]/g, function( ch ) {
+                       return "\\" + ch;
+               });
+       };
+
 /*
  * A number of helper functions used for managing events.
  * Many of the ideas behind this code originated from
@@ -18,177 +25,238 @@ jQuery.event = {
                        elem = window;
                }
 
-               // Make sure that the function being executed has a unique ID
-               if ( !handler.guid ) {
-                       handler.guid = this.guid++;
+               if ( handler === false ) {
+                       handler = returnFalse;
                }
 
-               // if data is passed, bind to handler
-               if ( data !== undefined ) {
-                       // Create temporary function pointer to original handler
-                       var fn = handler;
+               var handleObjIn, handleObj;
 
-                       // Create unique handler function, wrapped around original handler
-                       handler = this.proxy( fn );
+               if ( handler.handler ) {
+                       handleObjIn = handler;
+                       handler = handleObjIn.handler;
+               }
 
-                       // Store data in unique handler
-                       handler.data = data;
+               // Make sure that the function being executed has a unique ID
+               if ( !handler.guid ) {
+                       handler.guid = jQuery.guid++;
                }
 
                // Init the element's event structure
-               var events = jQuery.data( elem, "events" ) || jQuery.data( elem, "events", {} ),
-                       handle = jQuery.data( elem, "handle" ) || jQuery.data( elem, "handle", function eventHandle() {
+               var elemData = jQuery.data( elem );
+
+               // If no elemData is found then we must be trying to bind to one of the
+               // banned noData elements
+               if ( !elemData ) {
+                       return;
+               }
+
+               var events = elemData.events = elemData.events || {},
+                       eventHandle = elemData.handle;
+
+               if ( !eventHandle ) {
+                       elemData.handle = eventHandle = function() {
                                // Handle the second event of a trigger and when
                                // an event is called after a page has unloaded
                                return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
                                        jQuery.event.handle.apply( eventHandle.elem, arguments ) :
                                        undefined;
-                       });
+                       };
+               }
+
                // Add elem as a property of the handle function
-               // This is to prevent a memory leak with non-native
-               // event in IE.
-               handle.elem = elem;
+               // This is to prevent a memory leak with non-native events in IE.
+               eventHandle.elem = elem;
 
                // Handle multiple events separated by a space
                // jQuery(...).bind("mouseover mouseout", fn);
-               types = types.split( /\s+/ );
-               var type, i=0;
+               types = types.split(" ");
+
+               var type, i = 0, namespaces;
+
                while ( (type = types[ i++ ]) ) {
+                       handleObj = handleObjIn ?
+                               jQuery.extend({}, handleObjIn) :
+                               { handler: handler, data: data };
+
                        // Namespaced event handlers
-                       var namespaces = type.split(".");
-                       type = namespaces.shift();
-                       handler.type = namespaces.slice(0).sort().join(".");
+                       if ( type.indexOf(".") > -1 ) {
+                               namespaces = type.split(".");
+                               type = namespaces.shift();
+                               handleObj.namespace = namespaces.slice(0).sort().join(".");
+
+                       } else {
+                               namespaces = [];
+                               handleObj.namespace = "";
+                       }
+
+                       handleObj.type = type;
+                       if ( !handleObj.guid ) {
+                               handleObj.guid = handler.guid;
+                       }
 
                        // Get the current list of functions bound to this event
                        var handlers = events[ type ],
-                               special = this.special[ type ] || {};
-
-                       
+                               special = jQuery.event.special[ type ] || {};
 
                        // Init the event handler queue
                        if ( !handlers ) {
-                               handlers = events[ type ] = {};
+                               handlers = events[ type ] = [];
 
                                // 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, handler) === 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, handle, false );
+                                               elem.addEventListener( type, eventHandle, false );
+
                                        } else if ( elem.attachEvent ) {
-                                               elem.attachEvent( "on" + type, handle );
+                                               elem.attachEvent( "on" + type, eventHandle );
                                        }
                                }
                        }
                        
                        if ( special.add ) { 
-                               var modifiedHandler = special.add.call( elem, handler, data, namespaces, handlers ); 
-                               if ( modifiedHandler && jQuery.isFunction( modifiedHandler ) ) { 
-                                       modifiedHandler.guid = modifiedHandler.guid || handler.guid; 
-                                       handler = modifiedHandler; 
-                               } 
-                       } 
-                       
+                               special.add.call( elem, handleObj ); 
+
+                               if ( !handleObj.handler.guid ) {
+                                       handleObj.handler.guid = handler.guid;
+                               }
+                       }
+
                        // Add the function to the element's handler list
-                       handlers[ handler.guid ] = handler;
+                       handlers.push( handleObj );
 
                        // Keep track of which events have been used, for global triggering
-                       this.global[ type ] = true;
+                       jQuery.event.global[ type ] = true;
                }
 
                // Nullify elem to prevent memory leaks in IE
                elem = null;
        },
 
-       guid: 1,
        global: {},
 
        // Detach an event or set of events from an element
-       remove: function( elem, types, handler ) {
+       remove: function( elem, types, handler, pos ) {
                // don't do events on text and comment nodes
                if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
                        return;
                }
 
-               var events = jQuery.data( elem, "events" ), ret, type, fn;
+               if ( handler === false ) {
+                       handler = returnFalse;
+               }
 
-               if ( events ) {
-                       // Unbind all events for the element
-                       if ( types === undefined || (typeof types === "string" && types.charAt(0) === ".") ) {
-                               for ( type in events ) {
-                                       this.remove( elem, type + (types || "") );
-                               }
-                       } else {
-                               // types is actually an event object here
-                               if ( types.type ) {
-                                       handler = types.handler;
-                                       types = types.type;
+               var ret, type, fn, j, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType,
+                       elemData = jQuery.data( elem ),
+                       events = elemData && elemData.events;
+
+               if ( !elemData || !events ) {
+                       return;
+               }
+
+               // types is actually an event object here
+               if ( types && types.type ) {
+                       handler = types.handler;
+                       types = types.type;
+               }
+
+               // Unbind all events for the element
+               if ( !types || typeof types === "string" && types.charAt(0) === "." ) {
+                       types = types || "";
+
+                       for ( type in events ) {
+                               jQuery.event.remove( elem, type + types );
+                       }
+
+                       return;
+               }
+
+               // Handle multiple events separated by a space
+               // jQuery(...).unbind("mouseover mouseout", fn);
+               types = types.split(" ");
+
+               while ( (type = types[ i++ ]) ) {
+                       origType = type;
+                       handleObj = null;
+                       all = type.indexOf(".") < 0;
+                       namespaces = [];
+
+                       if ( !all ) {
+                               // Namespaced event handlers
+                               namespaces = type.split(".");
+                               type = namespaces.shift();
+
+                               namespace = new RegExp("(^|\\.)" + 
+                                       jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)");
+                       }
+
+                       eventType = events[ type ];
+
+                       if ( !eventType ) {
+                               continue;
+                       }
+
+                       if ( !handler ) {
+                               for ( j = 0; j < eventType.length; j++ ) {
+                                       handleObj = eventType[ j ];
+
+                                       if ( all || namespace.test( handleObj.namespace ) ) {
+                                               jQuery.event.remove( elem, origType, handleObj.handler, j );
+                                               eventType.splice( j--, 1 );
+                                       }
                                }
 
-                               // Handle multiple events separated by a space
-                               // jQuery(...).unbind("mouseover mouseout", fn);
-                               types = types.split(/\s+/);
-                               var i = 0;
-                               while ( (type = types[ i++ ]) ) {
-                                       // Namespaced event handlers
-                                       var namespaces = type.split(".");
-                                       type = namespaces.shift();
-                                       var all = !namespaces.length,
-                                               cleaned = jQuery.map( namespaces.slice(0).sort() , function(nm){ return nm.replace(/[^\w\s\.\|`]/g, function(ch){return "\\"+ch;  }); }),
-                                               namespace = new RegExp("(^|\\.)" + cleaned.join("\\.(?:.*\\.)?") + "(\\.|$)"),
-                                               special = this.special[ type ] || {};
-
-                                       if ( events[ type ] ) {
-                                               // remove the given handler for the given type
-                                               if ( handler ) {
-                                                       fn = events[ type ][ handler.guid ];
-                                                       delete events[ type ][ handler.guid ];
-
-                                               // remove all handlers for the given type
-                                               } else {
-                                                       for ( var handle in events[ type ] ) {
-                                                               // Handle the removal of namespaced events
-                                                               if ( all || namespace.test( events[ type ][ handle ].type ) ) {
-                                                                       delete events[ type ][ handle ];
-                                                               }
-                                                       }
+                               continue;
+                       }
+
+                       special = jQuery.event.special[ type ] || {};
+
+                       for ( j = pos || 0; j < eventType.length; j++ ) {
+                               handleObj = eventType[ j ];
+
+                               if ( handler.guid === handleObj.guid ) {
+                                       // remove the given handler for the given type
+                                       if ( all || namespace.test( handleObj.namespace ) ) {
+                                               if ( pos == null ) {
+                                                       eventType.splice( j--, 1 );
                                                }
 
                                                if ( special.remove ) {
-                                                       special.remove.call( elem, namespaces, fn);
+                                                       special.remove.call( elem, handleObj );
                                                }
+                                       }
 
-                                               // remove generic event handler if no more handlers exist
-                                               for ( ret in events[ type ] ) {
-                                                       break;
-                                               }
-                                               if ( !ret ) {
-                                                       if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
-                                                               if ( elem.removeEventListener ) {
-                                                                       elem.removeEventListener( type, jQuery.data( elem, "handle" ), false );
-                                                               } else if ( elem.detachEvent ) {
-                                                                       elem.detachEvent( "on" + type, jQuery.data( elem, "handle" ) );
-                                                               }
-                                                       }
-                                                       ret = null;
-                                                       delete events[ type ];
-                                               }
+                                       if ( pos != null ) {
+                                               break;
                                        }
                                }
                        }
 
-                       // Remove the expando if it's no longer used
-                       for ( ret in events ) {
-                               break;
-                       }
-                       if ( !ret ) {
-                               var handle = jQuery.data( elem, "handle" );
-                               if ( handle ) {
-                                       handle.elem = null;
+                       // 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.removeData( elem, "events" );
-                               jQuery.removeData( elem, "handle" );
+
+                               ret = null;
+                               delete events[ type ];
+                       }
+               }
+
+               // Remove the expando if it's no longer used
+               if ( jQuery.isEmptyObject( events ) ) {
+                       var handle = elemData.handle;
+                       if ( handle ) {
+                               handle.elem = null;
+                       }
+
+                       delete elemData.events;
+                       delete elemData.handle;
+
+                       if ( jQuery.isEmptyObject( elemData ) ) {
+                               jQuery.removeData( elem );
                        }
                }
        },
@@ -217,8 +285,9 @@ jQuery.event = {
                        if ( !elem ) {
                                // Don't bubble custom events when global (to avoid too much overhead)
                                event.stopPropagation();
+
                                // Only trigger if we've ever bound an event for it
-                               if ( this.global[ type ] ) {
+                               if ( jQuery.event.global[ type ] ) {
                                        jQuery.each( jQuery.cache, function() {
                                                if ( this.events && this.events[type] ) {
                                                        jQuery.event.trigger( event, data, this.handle.elem );
@@ -251,82 +320,105 @@ jQuery.event = {
                        handle.apply( elem, data );
                }
 
-               var nativeFn, nativeHandler;
+               var parent = elem.parentNode || elem.ownerDocument;
+
+               // Trigger an inline bound script
                try {
                        if ( !(elem && elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()]) ) {
-                               nativeFn = elem[ type ];
-                               nativeHandler = elem[ "on" + type ];
+                               if ( elem[ "on" + type ] && elem[ "on" + type ].apply( elem, data ) === false ) {
+                                       event.result = false;
+                               }
                        }
+
                // prevent IE from throwing an error for some elements with some event types, see #3533
-               } catch (e) {}
+               } catch (inlineError) {}
 
-               var isClick = jQuery.nodeName(elem, "a") && type === "click";
+               if ( !event.isPropagationStopped() && parent ) {
+                       jQuery.event.trigger( event, data, parent, true );
 
-               // Trigger the native events (except for clicks on links)
-               if ( !bubbling && nativeFn && !event.isDefaultPrevented() && !isClick ) {
-                       this.triggered = true;
-                       try {
-                               elem[ type ]();
-                       // prevent IE from throwing an error for some hidden elements
-                       } catch (e) {}
+               } else if ( !event.isDefaultPrevented() ) {
+                       var target = event.target, old, targetType = type.replace(/\..*$/, ""),
+                               isClick = jQuery.nodeName(target, "a") && targetType === "click",
+                               special = jQuery.event.special[ targetType ] || {};
 
-               // Handle triggering native .onfoo handlers
-               } else if ( nativeHandler && elem[ "on" + type ].apply( elem, data ) === false ) {
-                       event.result = false;
-               }
+                       if ( (!special._default || special._default.call( elem, event ) === false) && 
+                               !isClick && !(target && target.nodeName && jQuery.noData[target.nodeName.toLowerCase()]) ) {
 
-               this.triggered = false;
+                               try {
+                                       if ( target[ targetType ] ) {
+                                               // Make sure that we don't accidentally re-trigger the onFOO events
+                                               old = target[ "on" + targetType ];
 
-               if ( !event.isPropagationStopped() ) {
-                       var parent = elem.parentNode || elem.ownerDocument;
-                       if ( parent ) {
-                               jQuery.event.trigger( event, data, parent, true );
+                                               if ( old ) {
+                                                       target[ "on" + targetType ] = null;
+                                               }
+
+                                               jQuery.event.triggered = true;
+                                               target[ targetType ]();
+                                       }
+
+                               // prevent IE from throwing an error for some elements with some event types, see #3533
+                               } catch (triggerError) {}
+
+                               if ( old ) {
+                                       target[ "on" + targetType ] = old;
+                               }
+
+                               jQuery.event.triggered = false;
                        }
                }
        },
 
        handle: function( event ) {
-               // returned undefined or false
-               var all, handlers;
+               var all, handlers, namespaces, namespace_sort = [], namespace_re, events, args = jQuery.makeArray( arguments );
 
-               event = arguments[0] = jQuery.event.fix( event || window.event );
+               event = args[0] = jQuery.event.fix( event || window.event );
                event.currentTarget = this;
 
                // Namespaced event handlers
-               var namespaces = event.type.split(".");
-               event.type = namespaces.shift();
-
-               // Cache this now, all = true means, any handler
-               all = !namespaces.length && !event.exclusive;
+               all = event.type.indexOf(".") < 0 && !event.exclusive;
 
-               var namespace = new RegExp("(^|\\.)" + namespaces.slice(0).sort().join("\\.(?:.*\\.)?") + "(\\.|$)");
-
-               handlers = ( jQuery.data(this, "events") || {} )[ event.type ];
-
-               for ( var j in handlers ) {
-                       var handler = handlers[ j ];
-
-                       // Filter the functions by class
-                       if ( all || namespace.test(handler.type) ) {
-                               // Pass in a reference to the handler function itself
-                               // So that we can later remove it
-                               event.handler = handler;
-                               event.data = handler.data;
-
-                               var ret = handler.apply( this, arguments );
+               if ( !all ) {
+                       namespaces = event.type.split(".");
+                       event.type = namespaces.shift();
+                       namespace_sort = namespaces.slice(0).sort();
+                       namespace_re = new RegExp("(^|\\.)" + namespace_sort.join("\\.(?:.*\\.)?") + "(\\.|$)");
+               }
 
-                               if ( ret !== undefined ) {
-                                       event.result = ret;
-                                       if ( ret === false ) {
-                                               event.preventDefault();
-                                               event.stopPropagation();
+               event.namespace = event.namespace || namespace_sort.join(".");
+
+               events = jQuery.data(this, "events");
+               handlers = (events || {})[ event.type ];
+
+               if ( events && handlers ) {
+                       // Clone the handlers to prevent manipulation
+                       handlers = handlers.slice(0);
+
+                       for ( var j = 0, l = handlers.length; j < l; j++ ) {
+                               var handleObj = handlers[ j ];
+
+                               // Filter the functions by class
+                               if ( all || namespace_re.test( handleObj.namespace ) ) {
+                                       // Pass in a reference to the handler function itself
+                                       // So that we can later remove it
+                                       event.handler = handleObj.handler;
+                                       event.data = handleObj.data;
+                                       event.handleObj = handleObj;
+       
+                                       var ret = handleObj.handler.apply( this, args );
+
+                                       if ( ret !== undefined ) {
+                                               event.result = ret;
+                                               if ( ret === false ) {
+                                                       event.preventDefault();
+                                                       event.stopPropagation();
+                                               }
                                        }
-                               }
 
-                               if ( event.isImmediatePropagationStopped() ) {
-                                       break;
+                                       if ( event.isImmediatePropagationStopped() ) {
+                                               break;
+                                       }
                                }
-
                        }
                }
 
@@ -382,7 +474,7 @@ jQuery.event = {
                        event.metaKey = event.ctrlKey;
                }
 
-               // Add which for click: 1 == left; 2 == middle; 3 == right
+               // 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 !== undefined ) {
                        event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
@@ -391,63 +483,41 @@ jQuery.event = {
                return event;
        },
 
-       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
-               return proxy;
-       },
+       // Deprecated, use jQuery.guid instead
+       guid: 1E8,
+
+       // Deprecated, use jQuery.proxy instead
+       proxy: jQuery.proxy,
 
        special: {
                ready: {
                        // Make sure the ready event is setup
                        setup: jQuery.bindReady,
-                       teardown: function() {}
+                       teardown: jQuery.noop
                },
 
                live: {
-                       add: function( proxy, data, namespaces, live ) {
-                               jQuery.extend( proxy, data || {} );
-
-                               proxy.guid += data.selector + data.live; 
-                               jQuery.event.add( this, data.live, liveHandler, data ); 
-                               
+                       add: function( handleObj ) {
+                               jQuery.event.add( this,
+                                       liveConvert( handleObj.origType, handleObj.selector ),
+                                       jQuery.extend({}, handleObj, {handler: liveHandler, guid: handleObj.handler.guid}) ); 
                        },
 
-                       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 );
-                                       }
-                               }
-                       },
-                       special: {}
+                       remove: function( handleObj ) {
+                               jQuery.event.remove( this, liveConvert( handleObj.origType, handleObj.selector ), handleObj );
+                       }
                },
+
                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;
                                }
                        }
@@ -455,7 +525,19 @@ jQuery.event = {
        }
 };
 
-jQuery.Event = function( src ){
+var removeEvent = document.removeEventListener ?
+       function( elem, type, handle ) {
+               if ( elem.removeEventListener ) {
+                       elem.removeEventListener( type, handle, false );
+               }
+       } : 
+       function( elem, type, handle ) {
+               if ( elem.detachEvent ) {
+                       elem.detachEvent( "on" + type, handle );
+               }
+       };
+
+jQuery.Event = function( src ) {
        // Allow instantiation without the 'new' keyword
        if ( !this.preventDefault ) {
                return new jQuery.Event( src );
@@ -472,7 +554,7 @@ jQuery.Event = function( src ){
 
        // timeStamp is buggy for some events on Firefox(#3843)
        // So we won't rely on the native value
-       this.timeStamp = now();
+       this.timeStamp = jQuery.now();
 
        // Mark it as fixed
        this[ expando ] = true;
@@ -517,7 +599,7 @@ jQuery.Event.prototype = {
                // otherwise set the cancelBubble property of the original event to true (IE)
                e.cancelBubble = true;
        },
-       stopImmediatePropagation: function(){
+       stopImmediatePropagation: function() {
                this.isImmediatePropagationStopped = returnTrue;
                this.stopPropagation();
        },
@@ -525,27 +607,31 @@ jQuery.Event.prototype = {
        isPropagationStopped: returnFalse,
        isImmediatePropagationStopped: returnFalse
 };
+
 // Checks if an event happened on an element within another element
 // Used in jQuery.event.special.mouseenter and mouseleave handlers
 var withinElement = function( event ) {
        // Check if mouse(over|out) are still within the same parent element
        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; }
-               // assuming we've left the element since we most likely mousedover a xul element
-               catch(e) { break; }
-       }
 
-       if ( parent != this ) {
-               // set the correct event type
-               event.type = event.data;
-               // handle event if we actually just moused on to a non sub-element
-               jQuery.event.handle.apply( this, arguments );
-       }
+       // Firefox sometimes assigns relatedTarget a XUL element
+       // which we cannot access the parentNode property of
+       try {
+               // Traverse up the tree
+               while ( parent && parent !== this ) {
+                       parent = parent.parentNode;
+               }
+
+               if ( parent !== this ) {
+                       // set the correct event type
+                       event.type = event.data;
 
+                       // handle event if we actually just moused on to a non sub-element
+                       jQuery.event.handle.apply( this, arguments );
+               }
+
+       // assuming we've left the element since we most likely mousedover a xul element
+       } catch(e) { }
 },
 
 // In case of event delegation, we only need to rename the event.type,
@@ -561,10 +647,10 @@ jQuery.each({
        mouseleave: "mouseout"
 }, function( orig, fix ) {
        jQuery.event.special[ orig ] = {
-               setup: function(data){
+               setup: function( data ) {
                        jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig );
                },
-               teardown: function(data){
+               teardown: function( data ) {
                        jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement );
                }
        };
@@ -573,102 +659,142 @@ 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 );
+                                       }
+                               });
 
-       remove: function( namespaces, fn ) {
-               jQuery.event.remove( this, "click.specialSubmit" + (fn ? "."+fn.guid : "") );
-               jQuery.event.remove( this, "keypress.specialSubmit" + (fn ? "."+fn.guid : "") );
-       }
-};
+                       } else {
+                               return false;
+                       }
+               },
+
+               teardown: function( namespaces ) {
+                       jQuery.event.remove( this, ".specialSubmit" );
+               }
+       };
 
 }
 
 // change delegation, happens here so we have bind.
 if ( !jQuery.support.changeBubbles ) {
 
-jQuery.event.special.change = {
-       filters: {
-               click: function( e ) { 
-                       var elem = e.target;
+       var formElems = /textarea|input|select/i,
 
-                       if ( elem.nodeName.toLowerCase() === "input" && elem.type === "checkbox" ) {
-                               return trigger( "change", this, arguments );
-                       }
+       changeFilters,
+
+       getVal = function( elem ) {
+               var type = elem.type, val = elem.value;
 
-                       return changeFilters.keyup.call( this, e );
-               }, 
-               keyup: function( e ) { 
-                       var elem = e.target, data, index = elem.selectedIndex + "";
+               if ( type === "radio" || type === "checkbox" ) {
+                       val = elem.checked;
 
-                       if ( elem.nodeName.toLowerCase() === "select" ) {
-                               data = jQuery.data( elem, "_change_data" );
-                               jQuery.data( elem, "_change_data", index );
+               } else if ( type === "select-multiple" ) {
+                       val = elem.selectedIndex > -1 ?
+                               jQuery.map( elem.options, function( elem ) {
+                                       return elem.selected;
+                               }).join("-") :
+                               "";
+
+               } else if ( elem.nodeName.toLowerCase() === "select" ) {
+                       val = elem.selectedIndex;
+               }
+
+               return val;
+       },
 
-                               if ( (elem.type === "select-multiple" || data != null) && data !== index ) {
-                                       return trigger( "change", this, arguments );
+       testChange = function testChange( e ) {
+               var elem = e.target, data, val;
+
+               if ( !formElems.test( elem.nodeName ) || elem.readOnly ) {
+                       return;
+               }
+
+               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 );
+               }
+               
+               if ( data === undefined || val === data ) {
+                       return;
+               }
+
+               if ( data != null || val ) {
+                       e.type = "change";
+                       return jQuery.event.trigger( e, arguments[1], elem );
+               }
+       };
+
+       jQuery.event.special.change = {
+               filters: {
+                       focusout: testChange, 
+
+                       click: function( e ) {
+                               var elem = e.target, type = elem.type;
+
+                               if ( type === "radio" || type === "checkbox" || elem.nodeName.toLowerCase() === "select" ) {
+                                       return testChange.call( this, e );
                                }
-                       }
-               },
-               beforeactivate: function( e ) {
-                       var elem = e.target;
+                       },
+
+                       // 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 );
+                               }
+                       },
 
-                       if ( elem.nodeName.toLowerCase() === "input" && elem.type === "radio" && !elem.checked ) {
-                               return trigger( "change", this, arguments );
+                       // 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) );
                        }
                },
-               blur: function( e ) {
-                       var elem = e.target, nodeName = elem.nodeName.toLowerCase();
 
-                       if ( (nodeName === "textarea" || (nodeName === "input" && (elem.type === "text" || elem.type === "password")))
-                               && jQuery.data(elem, "_change_data") !== elem.value ) {
+               setup: function( data, namespaces ) {
+                       if ( this.type === "file" ) {
+                               return false;
+                       }
 
-                               return trigger( "change", this, arguments );
+                       for ( var type in changeFilters ) {
+                               jQuery.event.add( this, type + ".specialChange", changeFilters[type] );
                        }
+
+                       return formElems.test( this.nodeName );
                },
-               focus: function( e ) {
-                       var elem = e.target, nodeName = elem.nodeName.toLowerCase();
 
-                       if ( nodeName === "textarea" || (nodeName === "input" && (elem.type === "text" || elem.type === "password" ) ) ) {
-                               jQuery.data( elem, "_change_data", elem.value );
-                       }
-               }
-       },
-       setup: function( data, namespaces, fn ) {
-               for ( var type in changeFilters ) {
-                       jQuery.event.add( this, type + ".specialChange." + fn.guid, changeFilters[type] );
-               }
-               
-               // always want to listen for change for trigger
-               return false;
-       },
-       remove: function( namespaces, fn ) {
-               for ( var type in changeFilters ) {
-                       jQuery.event.remove( this, type + ".specialChange" + (fn ? "."+fn.guid : ""), changeFilters[type] );
-               }
-       }
-};
+               teardown: function( namespaces ) {
+                       jQuery.event.remove( this, ".specialChange" );
 
-var changeFilters = jQuery.event.special.change.filters;
+                       return formElems.test( this.nodeName );
+               }
+       };
 
+       changeFilters = jQuery.event.special.change.filters;
 }
 
 function trigger( type, elem, args ) {
@@ -677,28 +803,27 @@ function trigger( type, elem, args ) {
 }
 
 // Create "bubbling" focus and blur events
-if ( !jQuery.support.focusBubbles ) {
+if ( document.addEventListener ) {
+       jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
+               jQuery.event.special[ fix ] = {
+                       setup: function() {
+                               this.addEventListener( orig, handler, true );
+                       }, 
+                       teardown: function() { 
+                               this.removeEventListener( orig, handler, true );
+                       }
+               };
 
-jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ){
-       jQuery.event.special[ orig ] = {
-               setup: function() {
-                       jQuery.event.add( this, fix, ieHandler );
-               }, 
-               teardown: function() { 
-                       jQuery.event.remove( this, fix, ieHandler );
+               function handler( e ) { 
+                       e = jQuery.event.fix( e );
+                       e.type = fix;
+                       return jQuery.event.handle.call( this, e );
                }
-       };
-
-       function ieHandler() { 
-               arguments[0].type = orig;
-               return jQuery.event.handle.apply(this, arguments);
-       }
-});
-
+       });
 }
 
-jQuery.each(["bind", "one"], function(i, name) {
-       jQuery.fn[ name ] = function( type, data, fn, thisObject ) {
+jQuery.each(["bind", "one"], function( i, name ) {
+       jQuery.fn[ name ] = function( type, data, fn ) {
                // Handle object literals
                if ( typeof type === "object" ) {
                        for ( var key in type ) {
@@ -707,19 +832,26 @@ jQuery.each(["bind", "one"], function(i, name) {
                        return this;
                }
                
-               if ( jQuery.isFunction( data ) ) {
-                       thisObject = fn;
+               if ( jQuery.isFunction( data ) || data === false ) {
                        fn = data;
                        data = undefined;
                }
-               fn = thisObject === undefined ? fn : jQuery.event.proxy( fn, thisObject );
-               var handler = name == "one" ? jQuery.event.proxy( fn, function( event ) {
+
+               var handler = name === "one" ? jQuery.proxy( fn, function( event ) {
                        jQuery( this ).unbind( event, handler );
                        return fn.apply( this, arguments );
                }) : fn;
-               return type === "unload" ? this.one(type, data, handler, thisObject) : this.each(function() {
-                       jQuery.event.add( this, type, handler, data );
-               });
+
+               if ( type === "unload" && name !== "one" ) {
+                       this.one( type, data, fn );
+
+               } else {
+                       for ( var i = 0, l = this.length; i < l; i++ ) {
+                               jQuery.event.add( this[i], type, handler, data );
+                       }
+               }
+
+               return this;
        };
 });
 
@@ -730,13 +862,29 @@ jQuery.fn.extend({
                        for ( var key in type ) {
                                this.unbind(key, type[key]);
                        }
-                       return this;
+
+               } else {
+                       for ( var i = 0, l = this.length; i < l; i++ ) {
+                               jQuery.event.remove( this[i], type, fn );
+                       }
                }
 
-               return this.each(function() {
-                       jQuery.event.remove( this, type, fn );
-               });
+               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 );
@@ -758,14 +906,14 @@ jQuery.fn.extend({
                var args = arguments, i = 1;
 
                // link all the functions, so any of them can unbind this click handler
-               while( i < args.length ) {
-                       jQuery.event.proxy( fn, args[ i++ ] );
+               while ( i < args.length ) {
+                       jQuery.proxy( fn, args[ i++ ] );
                }
 
-               return this.click( jQuery.event.proxy( fn, function( event ) {
+               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();
@@ -777,64 +925,119 @@ jQuery.fn.extend({
 
        hover: function( fnOver, fnOut ) {
                return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
-       },
+       }
+});
+
+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, preType,
+                       selector = origSelector || this.selector,
+                       context = origSelector ? this : jQuery( this.context );
 
-       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: data, selector: this.selector, live: type
-               }, fn, thisObject );
-               return this;
-       },
 
-       die: function( type, fn ) {
-               jQuery( this.context ).unbind( liveConvert( type, this.selector ), fn ? { guid: fn.guid + this.selector + type } : null );
+               types = (types || "").split(" ");
+
+               while ( (type = types[ i++ ]) != null ) {
+                       match = rnamespaces.exec( type );
+                       namespaces = "";
+
+                       if ( match )  {
+                               namespaces = match[0];
+                               type = type.replace( rnamespaces, "" );
+                       }
+
+                       if ( type === "hover" ) {
+                               types.push( "mouseenter" + namespaces, "mouseleave" + namespaces );
+                               continue;
+                       }
+
+                       preType = type;
+
+                       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
+                               for ( var j = 0, l = context.length; j < l; j++ ) {
+                                       jQuery.event.add( context[j], "live." + liveConvert( type, selector ),
+                                               { data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } );
+                               }
+
+                       } else {
+                               // unbind live handler
+                               context.unbind( "live." + liveConvert( type, selector ), fn );
+                       }
+               }
+               
                return this;
-       }
+       };
 });
 
 function liveHandler( event ) {
-       var stop = true, elems = [], selectors = [], args = arguments,
-               related, match, fn, elem, j, i, data,
-               live = jQuery.extend({}, jQuery.data( this, "events" ).live);
+       var stop, maxLevel, elems = [], selectors = [],
+               related, match, handleObj, elem, j, i, l, data, close, namespace,
+               events = jQuery.data( this, "events" );
 
-       for ( j in live ) {
-               fn = live[j];
-               if ( fn.live === event.type ||
-                               fn.altLive && jQuery.inArray(event.type, fn.altLive) > -1 ) {
+       // Make sure we avoid non-left-click bubbling in Firefox (#3861)
+       if ( event.liveFired === this || !events || !events.live || event.button && event.type === "click" ) {
+               return;
+       }
+
+       if ( event.namespace ) {
+               namespace = new RegExp("(^|\\.)" + event.namespace.split(".").join("\\.(?:.*\\.)?") + "(\\.|$)");
+       }
+
+       event.liveFired = this;
+
+       var live = events.live.slice(0);
+
+       for ( j = 0; j < live.length; j++ ) {
+               handleObj = live[j];
+
+               if ( handleObj.origType.replace( rnamespaces, "" ) === event.type ) {
+                       selectors.push( handleObj.selector );
 
-                       data = fn.data;
-                       if ( !(data.beforeFilter && data.beforeFilter[event.type] && 
-                                       !data.beforeFilter[event.type](event)) ) {
-                               selectors.push( fn.selector );
-                       }
                } else {
-                       delete live[j];
+                       live.splice( j--, 1 );
                }
        }
 
        match = jQuery( event.target ).closest( selectors, event.currentTarget );
 
        for ( i = 0, l = match.length; i < l; i++ ) {
-               for ( j in live ) {
-                       fn = live[j];
-                       elem = match[i].elem;
-                       related = null;
+               close = match[i];
+
+               for ( j = 0; j < live.length; j++ ) {
+                       handleObj = live[j];
+
+                       if ( close.selector === handleObj.selector && (!namespace || namespace.test( handleObj.namespace )) ) {
+                               elem = close.elem;
+                               related = null;
 
-                       if ( match[i].selector === fn.selector ) {
                                // Those two events require additional checking
-                               if ( fn.live === "mouseenter" || fn.live === "mouseleave" ) {
-                                       related = jQuery( event.relatedTarget ).closest( fn.selector )[0];
+                               if ( handleObj.preType === "mouseenter" || handleObj.preType === "mouseleave" ) {
+                                       event.type = handleObj.preType;
+                                       related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0];
                                }
 
                                if ( !related || related !== elem ) {
-                                       elems.push({ elem: elem, fn: fn });
+                                       elems.push({ elem: elem, handleObj: handleObj, level: close.level });
                                }
                        }
                }
@@ -842,32 +1045,51 @@ function liveHandler( event ) {
 
        for ( i = 0, l = elems.length; i < l; i++ ) {
                match = elems[i];
-               event.currentTarget = match.elem;
-               event.data = match.fn.data;
-               if ( match.fn.apply( match.elem, args ) === false ) {
-                       stop = false;
+
+               if ( maxLevel && match.level > maxLevel ) {
                        break;
                }
+
+               event.currentTarget = match.elem;
+               event.data = match.handleObj.data;
+               event.handleObj = match.handleObj;
+
+               ret = match.handleObj.origHandler.apply( match.elem, arguments );
+
+               if ( ret === false || event.isPropagationStopped() ) {
+                       maxLevel = match.level;
+
+                       if ( ret === false ) {
+                               stop = false;
+                       }
+               }
        }
 
        return stop;
 }
 
 function liveConvert( type, selector ) {
-       return ["live", type, selector.replace(/\./g, "`").replace(/ /g, "&")].join(".");
+       return (type && type !== "*" ? type + "." : "") + selector.replace(/\./g, "`").replace(/ /g, "&");
 }
 
-jQuery.each( ("blur focus load resize scroll unload click dblclick " +
+jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
        "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
        "change select submit keydown keypress keyup error").split(" "), function( i, name ) {
 
        // Handle event binding
-       jQuery.fn[ name ] = function( fn ) {
-               return fn ? this.bind( name, fn ) : this.trigger( name );
+       jQuery.fn[ name ] = function( data, fn ) {
+               if ( fn == null ) {
+                       fn = data;
+                       data = null;
+               }
+
+               return arguments.length > 0 ?
+                       this.bind( name, data, fn ) :
+                       this.trigger( name );
        };
 
-       if ( jQuery.fnAttr ) {
-               jQuery.fnAttr[ name ] = true;
+       if ( jQuery.attrFn ) {
+               jQuery.attrFn[ name ] = true;
        }
 });
 
@@ -875,16 +1097,15 @@ jQuery.each( ("blur focus load resize scroll unload click dblclick " +
 // Window isn't included so as not to unbind existing unload events
 // More info:
 //  - http://isaacschlueter.com/2006/10/msie-memory-leaks/
-/*@cc_on
-jQuery( window ).bind( 'unload', function() {
-       for ( var id in jQuery.cache ) {
-               // Skip the window
-               if ( id != 1 && 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) {}
+if ( window.attachEvent && !window.addEventListener ) {
+       window.attachEvent("onunload", 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) {}
+                       }
                }
-       }
-});
-@*/
+       });
+}