No need to do the closest match if no selectors are passed in.
[jquery.git] / src / event.js
index f9c5fae..24e5ef1 100644 (file)
@@ -133,7 +133,7 @@ jQuery.event = {
                                        var namespaces = type.split(".");
                                        type = namespaces.shift();
                                        var all = !namespaces.length,
-                                               namespace = new RegExp("(^|\\.)" + namespaces.slice(0).sort().join(".*\\.") + "(\\.|$)"),
+                                               namespace = new RegExp("(^|\\.)" + namespaces.slice(0).sort().join("\\.(?:.*\\.)?") + "(\\.|$)"),
                                                special = this.special[ type ] || {};
 
                                        if ( events[ type ] ) {
@@ -291,7 +291,7 @@ jQuery.event = {
                // Cache this now, all = true means, any handler
                all = !namespaces.length && !event.exclusive;
 
-               var namespace = new RegExp("(^|\\.)" + namespaces.slice(0).sort().join(".*\\.") + "(\\.|$)");
+               var namespace = new RegExp("(^|\\.)" + namespaces.slice(0).sort().join("\\.(?:.*\\.)?") + "(\\.|$)");
 
                handlers = ( jQuery.data(this, "events") || {} )[ event.type ];
 
@@ -517,6 +517,14 @@ var withinElement = function( event ) {
                // handle event if we actually just moused on to a non sub-element
                jQuery.event.handle.apply( this, arguments );
        }
+
+},
+
+// In case of event delegation, we only need to rename the event.type,
+// liveHandler will take care of the rest.
+delegate = function( event ) {
+       event.type = event.data;
+       jQuery.event.handle.apply( this, arguments );
 };
 
 // Create mouseenter and mouseleave events
@@ -525,11 +533,11 @@ jQuery.each({
        mouseout: "mouseleave"
 }, function( orig, fix ) {
        jQuery.event.special[ fix ] = {
-               setup: function(){
-                       jQuery.event.add( this, orig, withinElement, fix );
+               setup: function(data){
+                       jQuery.event.add( this, orig, data && data.selector ? delegate : withinElement, fix );
                },
-               teardown: function(){
-                       jQuery.event.remove( this, orig, withinElement );
+               teardown: function(data){
+                       jQuery.event.remove( this, orig, data && data.selector ? delegate : withinElement );
                }
        };
 });
@@ -575,7 +583,6 @@ jQuery.each({
        blur: "focusout"
 }, function( orig, fix ){
        var event = jQuery.event,
-               special = event.special,
                handle = event.handle;
        
        function ieHandler() { 
@@ -583,18 +590,20 @@ jQuery.each({
                return handle.apply(this, arguments);
        }
 
-       special[orig] = {
+       event.special[orig] = {
                setup:function() {
-                       if ( this.addEventListener )
+                       if ( this.addEventListener ) {
                                this.addEventListener( orig, handle, true );
-                       else
-                               jQuery.event.add( this, fix, ieHandler );
+                       } else {
+                               event.add( this, fix, ieHandler );
+                       }
                }, 
                teardown:function() { 
-                       if ( this.removeEventListener )
+                       if ( this.removeEventListener ) {
                                this.removeEventListener( orig, handle, true );
-                       else
-                               jQuery.event.remove( this, fix, ieHandler );
+                       } else {
+                               event.remove( this, fix, ieHandler );
+                       }
                }
        };
 });
@@ -686,13 +695,14 @@ jQuery.fn.extend({
 
                return this.click( jQuery.event.proxy( fn, function( event ) {
                        // Figure out which function to execute
-                       this.lastToggle = ( this.lastToggle || 0 ) % i;
+                       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();
 
                        // and execute the function
-                       return args[ this.lastToggle++ ].apply( this, arguments ) || false;
+                       return args[ lastToggle ].apply( this, arguments ) || false;
                }));
        },
 
@@ -739,28 +749,53 @@ jQuery.fn.extend({
 });
 
 function liveHandler( event ) {
-       var stop = true, elems = [], args = arguments;
+       var stop = true, elems = [], selectors = [], args = arguments,
+               related, match, fn, elem, j, i,
+               live = jQuery.extend({}, jQuery.data( this, "events" ).live);
+
+       for ( j in live ) {
+               if ( live[j].live === event.type ) {
+                       selectors.push( live[j].selector );
+               }
+       }
+
+       console.log( event.type, selectors+"" );
+
+       // TODO: Make sure that duplicate selectors aren't run
+       match = jQuery( event.target ).closest( selectors, event.currentTarget );
+
+       console.log( "match", match, selectors+"" );
+
+       for ( i = 0, l = match.length; i < l; i++ ) {
+               for ( j in live ) {
+                       fn = live[j];
+                       elem = match[i].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];
+                               }
 
-       jQuery.each( jQuery.data( this, "events" ).live || [], function( i, fn ) {
-               if ( fn.live === event.type ) {
-                       var elem = jQuery( event.target ).closest( fn.selector )[0];
-                       if ( elem ) {
-                               elems.push({ elem: elem, fn: fn });
+                               if ( !related || related !== elem ) {
+                                       elems.push({ elem: elem, fn: fn });
+                               }
                        }
                }
-       });
+       }
 
-       elems.sort(function( a, b ) {
-               return jQuery.data( a.elem, "closest" ) - jQuery.data( b.elem, "closest" );
-       });
+       console.log( "elems", elems );
 
-       jQuery.each(elems, function() {
-               event.currentTarget = this.elem;
-               event.data = this.fn.data;
-               if ( this.fn.apply( this.elem, args ) === false ) {
-                       return (stop = false);
+       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;
+                       break;
                }
-       });
+       }
 
        return stop;
 }
@@ -776,6 +811,10 @@ jQuery.extend({
        ready: function() {
                // Make sure that the DOM is not already loaded
                if ( !jQuery.isReady ) {
+                       if ( !document.body ) {
+                               return setTimeout( jQuery.ready, 13 );
+                       }
+
                        // Remember that the DOM is ready
                        jQuery.isReady = true;
 
@@ -800,7 +839,7 @@ jQuery.extend({
 var readyBound = false;
 
 function bindReady() {
-       if ( readyBound ) return;
+       if ( readyBound ) { return; }
        readyBound = true;
 
        // Catch cases where $(document).ready() is called after the
@@ -823,32 +862,39 @@ function bindReady() {
                // maybe late but safe also for iframes
                document.attachEvent("onreadystatechange", function() {
                        // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
-                       if ( document.readyState === "complete" && document.body ) {
+                       if ( document.readyState === "complete" ) {
                                document.detachEvent( "onreadystatechange", arguments.callee );
                                jQuery.ready();
                        }
                });
 
-               // If IE and not an iframe
+               // If IE and not a frame
                // continually check to see if the document is ready
-               // NOTE: DO NOT CHANGE TO ===, FAILS IN IE.
-               if ( document.documentElement.doScroll && window == window.top ) (function() {
-                       if ( jQuery.isReady ) {
-                               return;
-                       }
+               var toplevel = false;
 
-                       try {
-                               // If IE is used, use the trick by Diego Perini
-                               // http://javascript.nwbox.com/IEContentLoaded/
-                               document.documentElement.doScroll("left");
-                       } catch( error ) {
-                               setTimeout( arguments.callee, 0 );
-                               return;
-                       }
+               try {
+                       toplevel = window.frameElement == null;
+               } catch(e){}
 
-                       // and execute any waiting functions
-                       jQuery.ready();
-               })();
+               if ( document.documentElement.doScroll && toplevel ) {
+                       (function() {
+                               if ( jQuery.isReady ) {
+                                       return;
+                               }
+
+                               try {
+                                       // If IE is used, use the trick by Diego Perini
+                                       // http://javascript.nwbox.com/IEContentLoaded/
+                                       document.documentElement.doScroll("left");
+                               } catch( error ) {
+                                       setTimeout( arguments.callee, 0 );
+                                       return;
+                               }
+
+                               // and execute any waiting functions
+                               jQuery.ready();
+                       })();
+               }
        }
 
        // A fallback to window.onload, that will always work