More bug fixes and added documentation - passes the test suite now.
[jquery.git] / event / event.js
1 jQuery.fn.extend({
2
3         // We're overriding the old toggle function, so
4         // remember it for later
5         _toggle: jQuery.fn.toggle,
6         
7         /**
8          * Toggle between two function calls every other click.
9          */
10         toggle: function(a,b) {
11                 // If two functions are passed in, we're
12                 // toggling on a click
13                 return a && b ? this.click(function(e){
14                         // Figure out which function to execute
15                         this.last = this.last == a ? b : a;
16                         
17                         // Make sure that clicks stop
18                         e.preventDefault();
19                         
20                         // and execute the function
21                         return this.last.apply( this, [e] ) || false;
22                 }) :
23                 
24                 // Otherwise, execute the old toggle function
25                 this._toggle();
26         },
27         
28         /**
29          * Toggle between two function calls on mouse over/out.
30          */
31         hover: function(f,g) {
32                 
33                 // A private function for haandling mouse 'hovering'
34                 function handleHover(e) {
35                         // Check if mouse(over|out) are still within the same parent element
36                         var p = (e.type == "mouseover" ? e.fromElement : e.toElement) || e.relatedTarget;
37         
38                         // Traverse up the tree
39                         while ( p && p != this ) p = p.parentNode;
40                         
41                         // If we actually just moused on to a sub-element, ignore it
42                         if ( p == this ) return false;
43                         
44                         // Execute the right function
45                         return (e.type == "mouseover" ? f : g).apply(this, [e]);
46                 }
47                 
48                 // Bind the function to the two event listeners
49                 return this.mouseover(handleHover).mouseout(handleHover);
50         },
51         
52         /**
53          * Bind a function to fire when the DOM is ready.
54          */
55         ready: function(f) {
56                 // If the DOM is already ready
57                 if ( jQuery.isReady )
58                         // Execute the function immediately
59                         f.apply( document );
60                         
61                 // Otherwise, remember the function for later
62                 else {
63                         // Add the function to the wait list
64                         jQuery.readyList.push( f );
65                 }
66         
67                 return this;
68         }
69 });
70
71 jQuery.extend({
72         /*
73          * All the code that makes DOM Ready work nicely.
74          */
75         isReady: false,
76         readyList: [],
77         
78         // Handle when the DOM is ready
79         ready: function() {
80                 // Make sure that the DOM is not already loaded
81                 if ( !jQuery.isReady ) {
82                         // Remember that the DOM is ready
83                         jQuery.isReady = true;
84                         
85                         // If there are functions bound, to execute
86                         if ( jQuery.readyList ) {
87                                 // Execute all of them
88                                 for ( var i = 0; i < jQuery.readyList.length; i++ )
89                                         jQuery.readyList[i].apply( document );
90                                 
91                                 // Reset the list of functions
92                                 jQuery.readyList = null;
93                         }
94                 }
95         }
96 });
97
98 new function(){
99         /*
100          * Bind a number of event-handling functions, dynamically
101          */
102         var e = ("blur,focus,load,resize,scroll,unload,click,dblclick," +
103                 "mousedown,mouseup,mousemove,mouseover,mouseout,change,reset,select," + 
104                 "submit,keydown,keypress,keyup,error").split(",");
105
106         // Go through all the event names, but make sure that
107         // it is enclosed properly
108         for ( var i = 0; i < e.length; i++ ) new function(){
109                         
110                 var o = e[i];
111                 
112                 // Handle event binding
113                 jQuery.fn[o] = function(f){
114                         return f ? this.bind(o, f) : this.trigger(o);
115                 };
116                 
117                 // Handle event unbinding
118                 jQuery.fn["un"+o] = function(f){ return this.unbind(o, f); };
119                 
120                 // Finally, handle events that only fire once
121                 jQuery.fn["one"+o] = function(f){
122                         // Attach the event listener
123                         return this.bind(o, function(e){
124                                 // TODO: Remove the event listener, instead of this hack
125                                 
126                                 // If this function has already been executed, stop
127                                 if ( this[o+f] !== null ) return;
128                                 
129                                 // Otherwise, mark as having been executed
130                                 this[o+f]++;
131                                 
132                                 // And execute the bound function
133                                 return f.apply(this, [e]);
134                         });
135                 };
136                         
137         }
138         
139         // If Mozilla is used
140         if ( jQuery.browser.mozilla || jQuery.browser.opera ) {
141                 // Use the handy event callback
142                 document.addEventListener( "DOMContentLoaded", jQuery.ready, false );
143         
144         // If IE is used, use the excellent hack by Matthias Miller
145         // http://www.outofhanwell.com/blog/index.php?title=the_window_onload_problem_revisited
146         } else if ( jQuery.browser.msie ) {
147         
148                 // Only works if you document.write() it
149                 document.write("<scr" + "ipt id=__ie_init defer=true " + 
150                         "src=https:///><\/script>");
151         
152                 // Use the defer script hack
153                 var script = document.getElementById("__ie_init");
154                 script.onreadystatechange = function() {
155                         if ( this.readyState == "complete" )
156                                 jQuery.ready();
157                 };
158         
159                 // Clear from memory
160                 script = null;
161         
162         // If Safari  is used
163         } else if ( jQuery.browser.safari ) {
164                 // Continually check to see if the document.readyState is valid
165                 jQuery.safariTimer = setInterval(function(){
166                         // loaded and complete are both valid states
167                         if ( document.readyState == "loaded" || 
168                                 document.readyState == "complete" ) {
169         
170                                 // If either one are found, remove the timer
171                                 clearInterval( jQuery.safariTimer );
172                                 jQuery.safariTimer = null;
173         
174                                 // and execute any waiting functions
175                                 jQuery.ready();
176                         }
177                 }, 10);
178         }
179         
180         // A fallback to window.onload, that will always work
181         jQuery.event.add( window, "load", jQuery.ready );
182         
183 }