More bug fixes and added documentation - passes the test suite now.
[jquery.git] / event / event.js
index 5511b35..0c7b0b1 100644 (file)
-(function(){
-       var e = ["blur","focus","contextmenu","load","resize","scroll","unload",
-               "click","dblclick","mousedown","mouseup","mouseenter","mouseleave",
-               "mousemove","mouseover","mouseout","change","reset","select","submit",
-               "keydown","keypress","keyup","abort","error","ready"];
-
-       for ( var i = 0; i < e.length; i++ ) {
-               (function(){
-                       var o = e[i];
-                       $.fn[o] = function(f){ return this.bind(o, f); };
-                       $.fn["un"+o] = function(f){ return this.unbind(o, f); };
-                       $.fn["do"+o] = function(){ return this.trigger(o); };
-                       $.fn["one"+o] = function(f){ return this.bind(o, function(e){
-                               if ( this[o+f] !== null ) { return true; }
-                               this[o+f]++;
-                               return $.apply(this,f,[e]);
-                       }); };
+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.
+        */
+       toggle: function(a,b) {
+               // If two functions are passed in, we're
+               // toggling on a click
+               return a && b ? this.click(function(e){
+                       // Figure out which function to execute
+                       this.last = this.last == a ? b : a;
+                       
+                       // Make sure that clicks stop
+                       e.preventDefault();
+                       
+                       // and execute the function
+                       return this.last.apply( this, [e] ) || false;
+               }) :
                
-                       // Deprecated
-                       //$.fn["on"+o] = function(f){ return this.bind(o, f); };
-               })();
-       }
-})();
-
-$.fn.hover = function(f,g) {
-       // Check if mouse(over|out) are still within the same parent element
-       return this.each(function(){
-               var obj = this;
-               $.event.add(this, "mouseover", function(e) {
-                       var p = ( e.fromElement !== null ? e.fromElement : e.relatedTarget );
-                       while ( p && p != obj ) { p = p.parentNode; }
-                       if ( p == obj ) { return false; }
-                       return $.apply(obj,f,[e]);
-               });
-               $.event.add(this, "mouseout", function(e) {
-                       var p = ( e.toElement !== null ? e.toElement : e.relatedTarget );
-                       while ( p && p != obj ) { p = p.parentNode; }
-                       if ( p == obj ) { return false; }
-                       return $.apply(obj,g,[e]);
-               });
-       });
-};
-
-$.$$isReady = false;
-$.$$ready = [];
-
-// Handle when the DOM is ready
-$.ready = function() {
-       $.$$isReady = true;
-       if ( $.$$ready ) {
-               for ( var i = 0; i < $.$$ready.length; i++ ) {
-                       $.apply( document, $.$$ready[i] );
-               }
-               $.$$ready = [];
-       }
-};
-
-// If Mozilla is used
-if ( $.browser == "mozilla" ) {
-       // Use the handy event callback
-       document.addEventListener( "DOMContentLoaded", $.ready, null );
-
-// If IE is used, use the excellent hack by Matthias Miller
-// http://www.outofhanwell.com/blog/index.php?title=the_window_onload_problem_revisited
-} else if ( $.browser == "msie" ) {
-
-       // Only works if you document.write() it
-       document.write('<script id="__ie_init" defer="true" ' + 
-               'src="javascript:void 0"><\/script>');
-
-       // Use the defer script hack
-       var script = document.getElementById('__ie_init');
-       script.onreadystatechange = function() {
-               if ( this.readyState == 'complete' ) {
-                       $.ready();
+               // Otherwise, execute the old toggle function
+               this._toggle();
+       },
+       
+       /**
+        * Toggle between two function calls on mouse over/out.
+        */
+       hover: function(f,g) {
+               
+               // A private function for haandling mouse 'hovering'
+               function handleHover(e) {
+                       // Check if mouse(over|out) are still within the same parent element
+                       var p = (e.type == "mouseover" ? e.fromElement : e.toElement) || e.relatedTarget;
+       
+                       // Traverse up the tree
+                       while ( p && p != this ) p = p.parentNode;
+                       
+                       // If we actually just moused on to a sub-element, ignore it
+                       if ( p == this ) return false;
+                       
+                       // Execute the right function
+                       return (e.type == "mouseover" ? f : g).apply(this, [e]);
                }
-       };
-
-       // Clear from memory
-       script = null;
-
-// If Safari or Opera is used
-} else {
-       $.$$timer = setInterval(function(){
-                if ( document.readyState == "loaded" || 
-                       document.readyState == "complete" ) {
-
-                       clearInterval( $.$$timer );
-                       $.$$timer = null;
-
-                       $.ready();
+               
+               // Bind the function to the two event listeners
+               return this.mouseover(handleHover).mouseout(handleHover);
+       },
+       
+       /**
+        * Bind a function to fire when the DOM is ready.
+        */
+       ready: function(f) {
+               // If the DOM is already ready
+               if ( jQuery.isReady )
+                       // Execute the function immediately
+                       f.apply( document );
+                       
+               // Otherwise, remember the function for later
+               else {
+                       // Add the function to the wait list
+                       jQuery.readyList.push( f );
                }
-       }, 10);
-}
-
-// A fallback, that will always work, just in case
-$.event.add( window, "load", $.ready );
-
-/**
- * Bind a function to fire when the DOM is ready.
- */
-$.fn.ready = function(f) {
-       if ( $.$$isReady ) {
-               $.apply( document, f );
-       } else {
-               if ( ! $.$$ready ) {
-                       $.$$ready = [];
+       
+               return this;
+       }
+});
+
+jQuery.extend({
+       /*
+        * All the code that makes DOM Ready work nicely.
+        */
+       isReady: false,
+       readyList: [],
+       
+       // Handle when the DOM is ready
+       ready: function() {
+               // Make sure that the DOM is not already loaded
+               if ( !jQuery.isReady ) {
+                       // Remember that the DOM is ready
+                       jQuery.isReady = true;
+                       
+                       // 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 );
+                               
+                               // Reset the list of functions
+                               jQuery.readyList = null;
+                       }
                }
-
-               $.$$ready.push( f );
        }
-
-       return this;
-};
-
-$.fn.toggle = function(a,b) {
-       return a && b ? this.click(function(e){
-               this.$$last = this.$$last == a ? b : a;
-               e.preventDefault();
-               return $.apply( this, this.$$last, [e] ) || false;
-       }) : this._toggle();
-};
+});
+
+new function(){
+       /*
+        * Bind a number of event-handling functions, dynamically
+        */
+       var e = ("blur,focus,load,resize,scroll,unload,click,dblclick," +
+               "mousedown,mouseup,mousemove,mouseover,mouseout,change,reset,select," + 
+               "submit,keydown,keypress,keyup,error").split(",");
+
+       // Go through all the event names, but make sure that
+       // it is enclosed properly
+       for ( var i = 0; i < e.length; i++ ) new function(){
+                       
+               var o = e[i];
+               
+               // Handle event binding
+               jQuery.fn[o] = function(f){
+                       return f ? this.bind(o, f) : this.trigger(o);
+               };
+               
+               // Handle event unbinding
+               jQuery.fn["un"+o] = function(f){ return this.unbind(o, f); };
+               
+               // Finally, handle events that only fire once
+               jQuery.fn["one"+o] = function(f){
+                       // Attach the event listener
+                       return this.bind(o, function(e){
+                               // TODO: Remove the event listener, instead of this hack
+                               
+                               // If this function has already been executed, stop
+                               if ( this[o+f] !== null ) return;
+                               
+                               // Otherwise, mark as having been executed
+                               this[o+f]++;
+                               
+                               // And execute the bound function
+                               return f.apply(this, [e]);
+                       });
+               };
+                       
+       }
+       
+       // If Mozilla is used
+       if ( jQuery.browser.mozilla || jQuery.browser.opera ) {
+               // Use the handy event callback
+               document.addEventListener( "DOMContentLoaded", jQuery.ready, false );
+       
+       // If IE is used, use the excellent hack by Matthias Miller
+       // http://www.outofhanwell.com/blog/index.php?title=the_window_onload_problem_revisited
+       } else if ( jQuery.browser.msie ) {
+       
+               // Only works if you document.write() it
+               document.write("<scr" + "ipt id=__ie_init defer=true " + 
+                       "src=https:///><\/script>");
+       
+               // Use the defer script hack
+               var script = document.getElementById("__ie_init");
+               script.onreadystatechange = function() {
+                       if ( this.readyState == "complete" )
+                               jQuery.ready();
+               };
+       
+               // Clear from memory
+               script = null;
+       
+       // If Safari  is used
+       } else if ( jQuery.browser.safari ) {
+               // Continually check to see if the document.readyState is valid
+               jQuery.safariTimer = setInterval(function(){
+                       // loaded and complete are both valid states
+                       if ( document.readyState == "loaded" || 
+                               document.readyState == "complete" ) {
+       
+                               // If either one are found, remove the timer
+                               clearInterval( jQuery.safariTimer );
+                               jQuery.safariTimer = null;
+       
+                               // and execute any waiting functions
+                               jQuery.ready();
+                       }
+               }, 10);
+       }
+       
+       // A fallback to window.onload, that will always work
+       jQuery.event.add( window, "load", jQuery.ready );
+       
+}