Don't remove test directory on clean
[jquery.git] / src / event.js
index 4775866..a380130 100644 (file)
@@ -8,6 +8,9 @@ jQuery.event = {
        // Bind an event to an element
        // Original by Dean Edwards
        add: function(element, type, handler, data) {
+               if ( element.nodeType == 3 || element.nodeType == 8 )
+                       return;
+
                // For whatever reason, IE has trouble passing the window object
                // around, causing it to be cloned in the process
                if ( jQuery.browser.msie && element.setInterval != undefined )
@@ -19,7 +22,7 @@ jQuery.event = {
                        
                // if data is passed, bind to handler 
                if( data != undefined ) { 
-                       // Create temporary function pointer to original handler 
+                       // Create temporary function pointer to original handler 
                        var fn = handler; 
 
                        // Create unique handler function, wrapped around original handler 
@@ -43,7 +46,7 @@ jQuery.event = {
                // Init the element's event structure
                var events = jQuery.data(element, "events") || jQuery.data(element, "events", {});
                
-               var handle = jQuery.data(element, "handle", function(){
+               var handle = jQuery.data(element, "handle") || jQuery.data(element, "handle", function(){
                        // returned undefined or false
                        var val;
 
@@ -83,6 +86,10 @@ jQuery.event = {
 
        // Detach an event or set of events from an element
        remove: function(element, type, handler) {
+               // don't do events on text and comment nodes
+               if ( element.nodeType == 3 || element.nodeType == 8 )
+                       return;
+
                var events = jQuery.data(element, "events"), ret, index;
 
                // Namespaced event handlers
@@ -147,12 +154,16 @@ jQuery.event = {
 
                // Handle triggering a single element
                } else {
+                       // don't do events on text and comment nodes
+                       if ( element.nodeType == 3 || element.nodeType == 8 )
+                               return;
+
                        var val, ret, fn = jQuery.isFunction( element[ type ] || null ),
                                // Check to see if we need to provide a fake event, or not
-                               evt = !data[0] || !data[0].preventDefault;
+                               event = !data[0] || !data[0].preventDefault;
                        
                        // Pass along a fake event
-                       if ( evt )
+                       if ( event )
                                data.unshift( this.fix({ type: type, target: element }) );
 
                        // Enforce the right trigger type
@@ -167,17 +178,25 @@ jQuery.event = {
                                val = false;
 
                        // Extra functions don't get the custom event object
-                       if ( evt )
+                       if ( event )
                                data.shift();
 
                        // Handle triggering of extra function
-                       if ( extra && extra.apply( element, data ) === false )
-                               val = false;
+                       if ( extra ) {
+                               // call the extra function and tack the current return value on the end for possible inspection
+                               var ret = extra.apply( element, data.concat( val ) );
+                               // if anything is returned, give it precedence and have it overwrite the previous value
+                               if (ret !== undefined)
+                                       val = ret;
+                       }
 
                        // Trigger the native events (except for clicks on links)
                        if ( fn && donative !== false && val !== false && !(jQuery.nodeName(element, 'a') && type == "click") ) {
                                this.triggered = true;
-                               element[ type ]();
+                               try {
+                                       element[ type ]();
+                               // prevent IE from throwing an error for some hidden elements
+                               } catch (e) {}
                        }
 
                        this.triggered = false;
@@ -197,23 +216,24 @@ jQuery.event = {
                var parts = event.type.split(".");
                event.type = parts[0];
 
-               var c = jQuery.data(this, "events") && jQuery.data(this, "events")[event.type], args = Array.prototype.slice.call( arguments, 1 );
+               var handlers = jQuery.data(this, "events") && jQuery.data(this, "events")[event.type], args = Array.prototype.slice.call( arguments, 1 );
                args.unshift( event );
 
-               for ( var j in c ) {
+               for ( var j in handlers ) {
+                       var handler = handlers[j];
                        // Pass in a reference to the handler function itself
                        // So that we can later remove it
-                       args[0].handler = c[j];
-                       args[0].data = c[j].data;
+                       args[0].handler = handler;
+                       args[0].data = handler.data;
 
                        // Filter the functions by class
-                       if ( !parts[1] || c[j].type == parts[1] ) {
-                               var tmp = c[j].apply( this, args );
+                       if ( !parts[1] || handler.type == parts[1] ) {
+                               var ret = handler.apply( this, args );
 
                                if ( val !== false )
-                                       val = tmp;
+                                       val = ret;
 
-                               if ( tmp === false ) {
+                               if ( ret === false ) {
                                        event.preventDefault();
                                        event.stopPropagation();
                                }
@@ -252,11 +272,11 @@ jQuery.event = {
                };
                
                // Fix target property, if necessary
-               if ( !event.target && event.srcElement )
-                       event.target = event.srcElement;
+               if ( !event.target )
+                       event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
                                
                // check if target is a textnode (safari)
-               if (jQuery.browser.safari && event.target.nodeType == 3)
+               if ( event.target.nodeType == 3 )
                        event.target = originalEvent.target.parentNode;
 
                // Add relatedTarget, if necessary
@@ -265,9 +285,9 @@ jQuery.event = {
 
                // Calculate pageX/Y if missing and clientX/Y available
                if ( event.pageX == null && event.clientX != null ) {
-                       var e = document.documentElement, b = document.body;
-                       event.pageX = event.clientX + (e && e.scrollLeft || b && b.scrollLeft || 0);
-                       event.pageY = event.clientY + (e && e.scrollTop || b && b.scrollTop || 0);
+                       var doc = document.documentElement, body = document.body;
+                       event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc.clientLeft || 0);
+                       event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc.clientTop || 0);
                }
                        
                // Add which for key events
@@ -322,54 +342,55 @@ jQuery.fn.extend({
 
        toggle: function() {
                // Save reference to arguments for access in closure
-               var a = arguments;
+               var args = arguments;
 
-               return this.click(function(e) {
+               return this.click(function(event) {
                        // Figure out which function to execute
                        this.lastToggle = 0 == this.lastToggle ? 1 : 0;
                        
                        // Make sure that clicks stop
-                       e.preventDefault();
+                       event.preventDefault();
                        
                        // and execute the function
-                       return a[this.lastToggle].apply( this, [e] ) || false;
+                       return args[this.lastToggle].apply( this, arguments ) || false;
                });
        },
 
-       hover: function(f,g) {
+       hover: function(fnOver, fnOut) {
                
                // A private function for handling mouse 'hovering'
-               function handleHover(e) {
+               function handleHover(event) {
                        // Check if mouse(over|out) are still within the same parent element
-                       var p = e.relatedTarget;
+                       var parent = event.relatedTarget;
        
                        // Traverse up the tree
-                       while ( p && p != this ) try { p = p.parentNode; } catch(e) { p = this; };
+                       while ( parent && parent != this ) try { parent = parent.parentNode; } catch(error) { parent = this; };
                        
                        // If we actually just moused on to a sub-element, ignore it
-                       if ( p == this ) return false;
+                       if ( parent == this ) 
+                               return true;
                        
                        // Execute the right function
-                       return (e.type == "mouseover" ? f : g).apply(this, [e]);
+                       return (event.type == "mouseover" ? fnOver : fnOut).apply(this, [event]);
                }
                
                // Bind the function to the two event listeners
                return this.mouseover(handleHover).mouseout(handleHover);
        },
        
-       ready: function(f) {
+       ready: function(fn) {
                // Attach the listeners
                bindReady();
 
                // If the DOM is already ready
                if ( jQuery.isReady )
                        // Execute the function immediately
-                       f.apply( document, [jQuery] );
+                       fn.apply( document, [jQuery] );
                        
                // Otherwise, remember the function for later
                else
                        // Add the function to the wait list
-                       jQuery.readyList.push( function() { return f.apply(this, [jQuery]); } );
+                       jQuery.readyList.push( function() { return fn.apply(this, [jQuery]); } );
        
                return this;
        }
@@ -400,7 +421,7 @@ jQuery.extend({
                                jQuery.readyList = null;
                        }
                        // Remove event listener to avoid memory leak
-                       if ( jQuery.browser.mozilla || jQuery.browser.opera )
+                       if ( document.removeEventListener )
                                document.removeEventListener( "DOMContentLoaded", jQuery.ready, false );
                }
        }
@@ -409,11 +430,11 @@ jQuery.extend({
 
 jQuery.each( ("blur,focus,load,resize,scroll,unload,click,dblclick," +
        "mousedown,mouseup,mousemove,mouseover,mouseout,change,select," + 
-       "submit,keydown,keypress,keyup,error").split(","), function(i,o){
+       "submit,keydown,keypress,keyup,error").split(","), function(i, name){
        
        // Handle event binding
-       jQuery.fn[o] = function(f){
-               return f ? this.bind(o, f) : this.trigger(o);
+       jQuery.fn[name] = function(fn){
+               return fn ? this.bind(name, fn) : this.trigger(name);
        };
 });
 
@@ -423,31 +444,34 @@ function bindReady(){
        if ( readyBound ) return;
        readyBound = true;
 
-       // If Mozilla is used
-       if ( jQuery.browser.mozilla || jQuery.browser.opera )
+       // Mozilla, Opera and webkit nightlies currently support this event
+       if ( document.addEventListener )
                // Use the handy event callback
                document.addEventListener( "DOMContentLoaded", jQuery.ready, false );
        
        // If Safari or IE is used
-       else {
-               // Continually check to see if the document is ready
-               function timer(){
-                       try {
-                               // If IE is used, use the trick by Diego Perini
-                               // http://javascript.nwbox.com/IEContentLoaded/
-                               if ( jQuery.browser.msie || document.readyState != "loaded" && document.readyState != "complete" )
-                                       document.documentElement.doScroll("left");
-       
-                               // and execute any waiting functions
-                               jQuery.ready();
-                       } catch( error ) {
-                               setTimeout( timer, 0 );
-                       }
+       // Continually check to see if the document is ready
+       if (jQuery.browser.msie || jQuery.browser.safari ) (function(){
+               try {
+                       // If IE is used, use the trick by Diego Perini
+                       // http://javascript.nwbox.com/IEContentLoaded/
+                       if ( jQuery.browser.msie || document.readyState != "loaded" && document.readyState != "complete" )
+                               document.documentElement.doScroll("left");
+               } catch( error ) {
+                       return setTimeout( arguments.callee, 0 );
                }
 
-               timer();
-       }
+               // and execute any waiting functions
+               jQuery.ready();
+       })();
 
        // A fallback to window.onload, that will always work
        jQuery.event.add( window, "load", jQuery.ready );
 }
+
+// Prevent memory leaks in IE
+// And prevent errors on refresh with events like mouseover
+// Window isn't included so as not to unbind existing unload events
+jQuery(window).bind("unload", function() {
+       jQuery("*").add(document).unbind();
+});