Fix for #964
[jquery.git] / src / event / event.js
index 9b3788b..fb7f820 100644 (file)
@@ -22,16 +22,16 @@ jQuery.event = {
                        handler.guid = this.guid++;
 
                // Init the element's event structure
-               if (!element.events)
-                       element.events = {};
+               if (!element.$events)
+                       element.$events = {};
 
                // Get the current list of functions bound to this event
-               var handlers = element.events[type];
+               var handlers = element.$events[type];
 
                // If it hasn't been initialized yet
                if (!handlers) {
                        // Init the event handler queue
-                       handlers = element.events[type] = {};
+                       handlers = element.$events[type] = {};
 
                        // Remember an existing handler, if it's already there
                        if (element["on" + type])
@@ -55,47 +55,73 @@ jQuery.event = {
 
        // Detach an event or set of events from an element
        remove: function(element, type, handler) {
-               if (element.events)
-                       if ( type && type.type )
-                               delete element.events[ type.type ][ type.handler.guid ];
-                       else if (type && element.events[type])
+               if (element.$events) {
+                       var i,j,k;
+                       if ( type && type.type ) { // type is actually an event object here
+                               handler = type.handler;
+                               type    = type.type;
+                       }
+                       
+                       if (type && element.$events[type])
+                               // remove the given handler for the given type
                                if ( handler )
-                                       delete element.events[type][handler.guid];
+                                       delete element.$events[type][handler.guid];
+                                       
+                               // remove all handlers for the given type
                                else
-                                       for ( var i in element.events[type] )
-                                               delete element.events[type][i];
+                                       for ( i in element.$events[type] )
+                                               delete element.$events[type][i];
+                                               
+                       // remove all handlers          
                        else
-                               for ( var j in element.events )
+                               for ( j in element.$events )
                                        this.remove( element, j );
+                       
+                       // remove event handler if no more handlers exist
+                       for ( k in element.$events[type] )
+                               if (k) {
+                                       k = true;
+                                       break;
+                               }
+                       if (!k) element["on" + type] = null;
+               }
        },
 
-       trigger: function(type,data,element) {
+       trigger: function(type, data, element) {
                // Clone the incoming data, if any
                data = jQuery.makeArray(data || []);
 
                // Handle a global trigger
-               if ( !element ) {
-                       var g = this.global[type];
-                       if ( g )
-                               for ( var i = 0, gl = g.length; i < gl; i++ )
-                                       this.trigger( type, data, g[i] );
+               if ( !element )
+                       jQuery.each( this.global[type] || [], function(){
+                               jQuery.event.trigger( type, data, this );
+                       });
 
                // Handle triggering a single element
-               } else if ( element["on" + type] ) {
-                       if ( element[ type ] && element[ type ].constructor == Function )
-                               element[ type ]();
-                       else {
+               else {
+                       var handler = element["on" + type ], val,
+                               fn = jQuery.isFunction( element[ type ] );
+
+                       if ( handler ) {
                                // Pass along a fake event
                                data.unshift( this.fix({ type: type, target: element }) );
        
                                // Trigger the event
-                               element["on" + type].apply( element, data );
+                               if ( (val = handler.apply( element, data )) !== false )
+                                       this.triggered = true;
                        }
+
+                       if ( fn && val !== false )
+                               element[ type ]();
+
+                       this.triggered = false;
                }
        },
 
        handle: function(event) {
-               if ( typeof jQuery == "undefined" ) return false;
+               // Handle the second event of a trigger and when
+               // an event is called after a page has unloaded
+               if ( typeof jQuery == "undefined" || jQuery.event.triggered ) return;
 
                // Empty object is for triggered events with no data
                event = jQuery.event.fix( event || window.event || {} ); 
@@ -103,7 +129,7 @@ jQuery.event = {
                // returned undefined or false
                var returnValue;
 
-               var c = this.events[event.type];
+               var c = this.$events[event.type];
 
                var args = [].slice.call( arguments, 1 );
                args.unshift( event );
@@ -133,7 +159,7 @@ jQuery.event = {
                        event.target = event.srcElement;
 
                // Calculate pageX/Y if missing and clientX/Y available
-               if ( typeof event.pageX == "undefined" && typeof event.clientX != "undefined" ) {
+               if ( event.pageX == undefined && event.clientX != undefined ) {
                        var e = document.documentElement, b = document.body;
                        event.pageX = event.clientX + (e.scrollLeft || b.scrollLeft);
                        event.pageY = event.clientY + (e.scrollTop || b.scrollTop);
@@ -187,32 +213,32 @@ jQuery.fn.extend({
         * data as the second paramter (and the handler function as the third), see 
         * second example.
         *
-        * @example $("p").bind( "click", function() {
+        * @example $("p").bind("click", function(){
         *   alert( $(this).text() );
-        * } )
+        * });
         * @before <p>Hello</p>
         * @result alert("Hello")
         *
-        * @example var handler = function(event) {
+        * @example function handler(event) {
         *   alert(event.data.foo);
-        * };
-        * $("p").bind( "click", {foo: "bar"}, handler)
+        * }
+        * $("p").bind("click", {foo: "bar"}, handler)
         * @result alert("bar")
         * @desc Pass some additional data to the event handler.
         *
-        * @example $("form").bind( "submit", function() { return false; } )
+        * @example $("form").bind("submit", function() { return false; })
         * @desc Cancel a default action and prevent it from bubbling by returning false
         * from your function.
         *
-        * @example $("form").bind( "submit", function(event) {
+        * @example $("form").bind("submit", function(event){
         *   event.preventDefault();
-        * } );
+        * });
         * @desc Cancel only the default action by using the preventDefault method.
         *
         *
-        * @example $("form").bind( "submit", function(event) {
+        * @example $("form").bind("submit", function(event){
         *   event.stopPropagation();
-        * } )
+        * });
         * @desc Stop only an event from bubbling by using the stopPropagation method.
         *
         * @name bind
@@ -241,9 +267,9 @@ jQuery.fn.extend({
         * data as the second paramter (and the handler function as the third), see 
         * second example.
         *
-        * @example $("p").one( "click", function() {
+        * @example $("p").one("click", function(){
         *   alert( $(this).text() );
-        * } )
+        * });
         * @before <p>Hello</p>
         * @result alert("Hello")
         *
@@ -305,9 +331,19 @@ jQuery.fn.extend({
         * @before <p click="alert('hello')">Hello</p>
         * @result alert('hello')
         *
+        * @example $("p").click(function(event, a, b) {
+        *   // when a normal click fires, a and b are undefined
+        *   // for a trigger like below a refers too "foo" and b refers to "bar"
+        * }).trigger("click", ["foo", "bar"]);
+        * @desc Example of how to pass arbitrary to an event
+        * 
+        * @before <p click="alert('hello')">Hello</p>
+        * @result alert('hello')
+        *
         * @name trigger
         * @type jQuery
         * @param String type An event type to trigger.
+        * @param Array data (optional) Additional data to pass as arguments (after the event object) to the event handler
         * @cat Events
         */
        trigger: function( type, data ) {
@@ -316,10 +352,6 @@ jQuery.fn.extend({
                });
        },
 
-       // We're overriding the old toggle function, so
-       // remember it for later
-       _toggle: jQuery.fn.toggle,
-       
        /**
         * Toggle between two function calls every other click.
         * Whenever a matched element is clicked, the first specified function 
@@ -341,9 +373,10 @@ jQuery.fn.extend({
         * @cat Events
         */
        toggle: function() {
-               // save reference to arguments for access in closure
+               // Save reference to arguments for access in closure
                var a = arguments;
-               return typeof a[0] == "function" && typeof a[1] == "function" ? this.click(function(e) {
+
+               return this.click(function(e) {
                        // Figure out which function to execute
                        this.lastToggle = this.lastToggle == 0 ? 1 : 0;
                        
@@ -352,10 +385,7 @@ jQuery.fn.extend({
                        
                        // and execute the function
                        return a[this.lastToggle].apply( this, [e] ) || false;
-               }) :
-               
-               // Otherwise, execute the old toggle function
-               this._toggle.apply( this, arguments );
+               });
        },
        
        /**
@@ -414,6 +444,10 @@ jQuery.fn.extend({
         * and attaching a function to that. By using this method, your bound Function 
         * will be called the instant the DOM is ready to be read and manipulated, 
         * which is exactly what 99.99% of all Javascript code needs to run.
+        *
+        * There is one argument passed to the ready event handler: A reference to
+        * the jQuery function. You can name that argument whatever you like, and
+        * can therefore stick with the $ alias without risk of naming collisions.
         * 
         * Please ensure you have no code in your &lt;body&gt; onload event handler, 
         * otherwise $(document).ready() may not fire.
@@ -423,21 +457,30 @@ jQuery.fn.extend({
         *
         * @example $(document).ready(function(){ Your code here... });
         *
+        * @example jQuery(function($) {
+        *   // Your code using failsafe $ alias here...
+        * });
+        * @desc Uses both the shortcut for $(document).ready() and the argument
+        * to write failsafe jQuery code using the $ alias, without relying on the
+        * global alias.
+        *
         * @name ready
         * @type jQuery
         * @param Function fn The function to be executed when the DOM is ready.
         * @cat Events
+        * @see $.noConflict()
+        * @see $(Function)
         */
        ready: function(f) {
                // If the DOM is already ready
                if ( jQuery.isReady )
                        // Execute the function immediately
-                       f.apply( document );
+                       f.apply( document, [jQuery] );
                        
                // Otherwise, remember the function for later
                else {
                        // Add the function to the wait list
-                       jQuery.readyList.push( f );
+                       jQuery.readyList.push( function() { return f.apply(this, [jQuery]) } );
                }
        
                return this;
@@ -461,8 +504,9 @@ jQuery.extend({
                        // If there are functions bound, to execute
                        if ( jQuery.readyList ) {
                                // Execute all of them
-                               for ( var i = 0; i < jQuery.readyList.length; i++ )
-                                       jQuery.readyList[i].apply( document );
+                               jQuery.each( jQuery.readyList, function(){
+                                       this.apply( document );
+                               });
                                
                                // Reset the list of functions
                                jQuery.readyList = null;
@@ -486,7 +530,7 @@ new function(){
         * @name scroll
         * @type jQuery
         * @param Function fn A function to bind to the scroll event on each of the matched elements.
-        * @cat Events/Browser
+        * @cat Events
         */
 
        /**
@@ -501,7 +545,7 @@ new function(){
         * @name submit
         * @type jQuery
         * @param Function fn A function to bind to the submit event on each of the matched elements.
-        * @cat Events/Form
+        * @cat Events
         */
 
        /**
@@ -516,7 +560,7 @@ new function(){
         *
         * @name submit
         * @type jQuery
-        * @cat Events/Form
+        * @cat Events
         */
 
        /**
@@ -529,7 +573,7 @@ new function(){
         * @name focus
         * @type jQuery
         * @param Function fn A function to bind to the focus event on each of the matched elements.
-        * @cat Events/UI
+        * @cat Events
         */
 
        /**
@@ -545,7 +589,7 @@ new function(){
         *
         * @name focus
         * @type jQuery
-        * @cat Events/UI
+        * @cat Events
         */
 
        /**
@@ -558,7 +602,7 @@ new function(){
         * @name keydown
         * @type jQuery
         * @param Function fn A function to bind to the keydown event on each of the matched elements.
-        * @cat Events/Keyboard
+        * @cat Events
         */
 
        /**
@@ -571,7 +615,7 @@ new function(){
         * @name dblclick
         * @type jQuery
         * @param Function fn A function to bind to the dblclick event on each of the matched elements.
-        * @cat Events/Mouse
+        * @cat Events
         */
 
        /**
@@ -584,7 +628,7 @@ new function(){
         * @name keypress
         * @type jQuery
         * @param Function fn A function to bind to the keypress event on each of the matched elements.
-        * @cat Events/Keyboard
+        * @cat Events
         */
 
        /**
@@ -597,7 +641,7 @@ new function(){
         * @name error
         * @type jQuery
         * @param Function fn A function to bind to the error event on each of the matched elements.
-        * @cat Events/Browser
+        * @cat Events
         */
 
        /**
@@ -610,7 +654,7 @@ new function(){
         * @name blur
         * @type jQuery
         * @param Function fn A function to bind to the blur event on each of the matched elements.
-        * @cat Events/UI
+        * @cat Events
         */
 
        /**
@@ -626,7 +670,7 @@ new function(){
         *
         * @name blur
         * @type jQuery
-        * @cat Events/UI
+        * @cat Events
         */
 
        /**
@@ -639,7 +683,7 @@ new function(){
         * @name load
         * @type jQuery
         * @param Function fn A function to bind to the load event on each of the matched elements.
-        * @cat Events/Browser
+        * @cat Events
         */
 
        /**
@@ -652,7 +696,7 @@ new function(){
         * @name select
         * @type jQuery
         * @param Function fn A function to bind to the select event on each of the matched elements.
-        * @cat Events/Form
+        * @cat Events
         */
 
        /**
@@ -665,7 +709,7 @@ new function(){
         *
         * @name select
         * @type jQuery
-        * @cat Events/Form
+        * @cat Events
         */
 
        /**
@@ -678,7 +722,7 @@ new function(){
         * @name mouseup
         * @type jQuery
         * @param Function fn A function to bind to the mouseup event on each of the matched elements.
-        * @cat Events/Mouse
+        * @cat Events
         */
 
        /**
@@ -691,7 +735,7 @@ new function(){
         * @name unload
         * @type jQuery
         * @param Function fn A function to bind to the unload event on each of the matched elements.
-        * @cat Events/Browser
+        * @cat Events
         */
 
        /**
@@ -704,7 +748,7 @@ new function(){
         * @name change
         * @type jQuery
         * @param Function fn A function to bind to the change event on each of the matched elements.
-        * @cat Events/Form
+        * @cat Events
         */
 
        /**
@@ -717,7 +761,7 @@ new function(){
         * @name mouseout
         * @type jQuery
         * @param Function fn A function to bind to the mouseout event on each of the matched elements.
-        * @cat Events/Mouse
+        * @cat Events
         */
 
        /**
@@ -730,7 +774,7 @@ new function(){
         * @name keyup
         * @type jQuery
         * @param Function fn A function to bind to the keyup event on each of the matched elements.
-        * @cat Events/Keyboard
+        * @cat Events
         */
 
        /**
@@ -743,7 +787,7 @@ new function(){
         * @name click
         * @type jQuery
         * @param Function fn A function to bind to the click event on each of the matched elements.
-        * @cat Events/Mouse
+        * @cat Events
         */
 
        /**
@@ -756,7 +800,7 @@ new function(){
         *
         * @name click
         * @type jQuery
-        * @cat Events/Mouse
+        * @cat Events
         */
 
        /**
@@ -769,7 +813,7 @@ new function(){
         * @name resize
         * @type jQuery
         * @param Function fn A function to bind to the resize event on each of the matched elements.
-        * @cat Events/Browser
+        * @cat Events
         */
 
        /**
@@ -782,7 +826,7 @@ new function(){
         * @name mousemove
         * @type jQuery
         * @param Function fn A function to bind to the mousemove event on each of the matched elements.
-        * @cat Events/Mouse
+        * @cat Events
         */
 
        /**
@@ -795,7 +839,7 @@ new function(){
         * @name mousedown
         * @type jQuery
         * @param Function fn A function to bind to the mousedown event on each of the matched elements.
-        * @cat Events/Mouse
+        * @cat Events
         */
         
        /**
@@ -808,7 +852,7 @@ new function(){
         * @name mouseover
         * @type jQuery
         * @param Function fn A function to bind to the mousedown event on each of the matched elements.
-        * @cat Events/Mouse
+        * @cat Events
         */
        jQuery.each( ("blur,focus,load,resize,scroll,unload,click,dblclick," +
                "mousedown,mouseup,mousemove,mouseover,mouseout,change,select," + 
@@ -881,4 +925,4 @@ if (jQuery.browser.msie)
                                        jQuery.event.remove(els[i-1], type);
                                while (--i);
                }
-       });
\ No newline at end of file
+       });