Merge branch 'bug7531' of https://github.com/csnover/jquery into csnover-bug7531
[jquery.git] / src / event.js
1 (function( jQuery ) {
2
3 var rnamespaces = /\.(.*)$/,
4         rformElems = /^(?:textarea|input|select)$/i,
5         rperiod = /\./g,
6         rspace = / /g,
7         rescape = /[^\w\s.|`]/g,
8         fcleanup = function( nm ) {
9                 return nm.replace(rescape, "\\$&");
10         },
11         focusCounts = { focusin: 0, focusout: 0 };
12
13 /*
14  * A number of helper functions used for managing events.
15  * Many of the ideas behind this code originated from
16  * Dean Edwards' addEvent library.
17  */
18 jQuery.event = {
19
20         // Bind an event to an element
21         // Original by Dean Edwards
22         add: function( elem, types, handler, data ) {
23                 if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
24                         return;
25                 }
26
27                 // For whatever reason, IE has trouble passing the window object
28                 // around, causing it to be cloned in the process
29                 if ( jQuery.isWindow( elem ) && ( elem !== window && !elem.frameElement ) ) {
30                         elem = window;
31                 }
32
33                 if ( handler === false ) {
34                         handler = returnFalse;
35                 } else if ( !handler ) {
36                         // Fixes bug #7229. Fix recommended by jdalton
37                   return;
38                 }
39
40                 var handleObjIn, handleObj;
41
42                 if ( handler.handler ) {
43                         handleObjIn = handler;
44                         handler = handleObjIn.handler;
45                 }
46
47                 // Make sure that the function being executed has a unique ID
48                 if ( !handler.guid ) {
49                         handler.guid = jQuery.guid++;
50                 }
51
52                 // Init the element's event structure
53                 var elemData = jQuery.data( elem );
54
55                 // If no elemData is found then we must be trying to bind to one of the
56                 // banned noData elements
57                 if ( !elemData ) {
58                         return;
59                 }
60
61                 // Use a key less likely to result in collisions for plain JS objects.
62                 // Fixes bug #7150.
63                 var eventKey = elem.nodeType ? "events" : "__events__",
64                         events = elemData[ eventKey ],
65                         eventHandle = elemData.handle;
66                         
67                 if ( typeof events === "function" ) {
68                         // On plain objects events is a fn that holds the the data
69                         // which prevents this data from being JSON serialized
70                         // the function does not need to be called, it just contains the data
71                         eventHandle = events.handle;
72                         events = events.events;
73
74                 } else if ( !events ) {
75                         if ( !elem.nodeType ) {
76                                 // On plain objects, create a fn that acts as the holder
77                                 // of the values to avoid JSON serialization of event data
78                                 elemData[ eventKey ] = elemData = function(){};
79                         }
80
81                         elemData.events = events = {};
82                 }
83
84                 if ( !eventHandle ) {
85                         elemData.handle = eventHandle = function() {
86                                 // Handle the second event of a trigger and when
87                                 // an event is called after a page has unloaded
88                                 return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
89                                         jQuery.event.handle.apply( eventHandle.elem, arguments ) :
90                                         undefined;
91                         };
92                 }
93
94                 // Add elem as a property of the handle function
95                 // This is to prevent a memory leak with non-native events in IE.
96                 eventHandle.elem = elem;
97
98                 // Handle multiple events separated by a space
99                 // jQuery(...).bind("mouseover mouseout", fn);
100                 types = types.split(" ");
101
102                 var type, i = 0, namespaces;
103
104                 while ( (type = types[ i++ ]) ) {
105                         handleObj = handleObjIn ?
106                                 jQuery.extend({}, handleObjIn) :
107                                 { handler: handler, data: data };
108
109                         // Namespaced event handlers
110                         if ( type.indexOf(".") > -1 ) {
111                                 namespaces = type.split(".");
112                                 type = namespaces.shift();
113                                 handleObj.namespace = namespaces.slice(0).sort().join(".");
114
115                         } else {
116                                 namespaces = [];
117                                 handleObj.namespace = "";
118                         }
119
120                         handleObj.type = type;
121                         if ( !handleObj.guid ) {
122                                 handleObj.guid = handler.guid;
123                         }
124
125                         // Get the current list of functions bound to this event
126                         var handlers = events[ type ],
127                                 special = jQuery.event.special[ type ] || {};
128
129                         // Init the event handler queue
130                         if ( !handlers ) {
131                                 handlers = events[ type ] = [];
132
133                                 // Check for a special event handler
134                                 // Only use addEventListener/attachEvent if the special
135                                 // events handler returns false
136                                 if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
137                                         // Bind the global event handler to the element
138                                         if ( elem.addEventListener ) {
139                                                 elem.addEventListener( type, eventHandle, false );
140
141                                         } else if ( elem.attachEvent ) {
142                                                 elem.attachEvent( "on" + type, eventHandle );
143                                         }
144                                 }
145                         }
146                         
147                         if ( special.add ) { 
148                                 special.add.call( elem, handleObj ); 
149
150                                 if ( !handleObj.handler.guid ) {
151                                         handleObj.handler.guid = handler.guid;
152                                 }
153                         }
154
155                         // Add the function to the element's handler list
156                         handlers.push( handleObj );
157
158                         // Keep track of which events have been used, for global triggering
159                         jQuery.event.global[ type ] = true;
160                 }
161
162                 // Nullify elem to prevent memory leaks in IE
163                 elem = null;
164         },
165
166         global: {},
167
168         // Detach an event or set of events from an element
169         remove: function( elem, types, handler, pos ) {
170                 // don't do events on text and comment nodes
171                 if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
172                         return;
173                 }
174
175                 if ( handler === false ) {
176                         handler = returnFalse;
177                 }
178
179                 var ret, type, fn, j, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType,
180                         eventKey = elem.nodeType ? "events" : "__events__",
181                         elemData = jQuery.data( elem ),
182                         events = elemData && elemData[ eventKey ];
183
184                 if ( !elemData || !events ) {
185                         return;
186                 }
187                 
188                 if ( typeof events === "function" ) {
189                         elemData = events;
190                         events = events.events;
191                 }
192
193                 // types is actually an event object here
194                 if ( types && types.type ) {
195                         handler = types.handler;
196                         types = types.type;
197                 }
198
199                 // Unbind all events for the element
200                 if ( !types || typeof types === "string" && types.charAt(0) === "." ) {
201                         types = types || "";
202
203                         for ( type in events ) {
204                                 jQuery.event.remove( elem, type + types );
205                         }
206
207                         return;
208                 }
209
210                 // Handle multiple events separated by a space
211                 // jQuery(...).unbind("mouseover mouseout", fn);
212                 types = types.split(" ");
213
214                 while ( (type = types[ i++ ]) ) {
215                         origType = type;
216                         handleObj = null;
217                         all = type.indexOf(".") < 0;
218                         namespaces = [];
219
220                         if ( !all ) {
221                                 // Namespaced event handlers
222                                 namespaces = type.split(".");
223                                 type = namespaces.shift();
224
225                                 namespace = new RegExp("(^|\\.)" + 
226                                         jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)");
227                         }
228
229                         eventType = events[ type ];
230
231                         if ( !eventType ) {
232                                 continue;
233                         }
234
235                         if ( !handler ) {
236                                 for ( j = 0; j < eventType.length; j++ ) {
237                                         handleObj = eventType[ j ];
238
239                                         if ( all || namespace.test( handleObj.namespace ) ) {
240                                                 jQuery.event.remove( elem, origType, handleObj.handler, j );
241                                                 eventType.splice( j--, 1 );
242                                         }
243                                 }
244
245                                 continue;
246                         }
247
248                         special = jQuery.event.special[ type ] || {};
249
250                         for ( j = pos || 0; j < eventType.length; j++ ) {
251                                 handleObj = eventType[ j ];
252
253                                 if ( handler.guid === handleObj.guid ) {
254                                         // remove the given handler for the given type
255                                         if ( all || namespace.test( handleObj.namespace ) ) {
256                                                 if ( pos == null ) {
257                                                         eventType.splice( j--, 1 );
258                                                 }
259
260                                                 if ( special.remove ) {
261                                                         special.remove.call( elem, handleObj );
262                                                 }
263                                         }
264
265                                         if ( pos != null ) {
266                                                 break;
267                                         }
268                                 }
269                         }
270
271                         // remove generic event handler if no more handlers exist
272                         if ( eventType.length === 0 || pos != null && eventType.length === 1 ) {
273                                 if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
274                                         jQuery.removeEvent( elem, type, elemData.handle );
275                                 }
276
277                                 ret = null;
278                                 delete events[ type ];
279                         }
280                 }
281
282                 // Remove the expando if it's no longer used
283                 if ( jQuery.isEmptyObject( events ) ) {
284                         var handle = elemData.handle;
285                         if ( handle ) {
286                                 handle.elem = null;
287                         }
288
289                         delete elemData.events;
290                         delete elemData.handle;
291
292                         if ( typeof elemData === "function" ) {
293                                 jQuery.removeData( elem, eventKey );
294
295                         } else if ( jQuery.isEmptyObject( elemData ) ) {
296                                 jQuery.removeData( elem );
297                         }
298                 }
299         },
300
301         // bubbling is internal
302         trigger: function( event, data, elem /*, bubbling */ ) {
303                 // Event object or event type
304                 var type = event.type || event,
305                         bubbling = arguments[3];
306
307                 if ( !bubbling ) {
308                         event = typeof event === "object" ?
309                                 // jQuery.Event object
310                                 event[ jQuery.expando ] ? event :
311                                 // Object literal
312                                 jQuery.extend( jQuery.Event(type), event ) :
313                                 // Just the event type (string)
314                                 jQuery.Event(type);
315
316                         if ( type.indexOf("!") >= 0 ) {
317                                 event.type = type = type.slice(0, -1);
318                                 event.exclusive = true;
319                         }
320
321                         // Handle a global trigger
322                         if ( !elem ) {
323                                 // Don't bubble custom events when global (to avoid too much overhead)
324                                 event.stopPropagation();
325
326                                 // Only trigger if we've ever bound an event for it
327                                 if ( jQuery.event.global[ type ] ) {
328                                         jQuery.each( jQuery.cache, function() {
329                                                 if ( this.events && this.events[type] ) {
330                                                         jQuery.event.trigger( event, data, this.handle.elem );
331                                                 }
332                                         });
333                                 }
334                         }
335
336                         // Handle triggering a single element
337
338                         // don't do events on text and comment nodes
339                         if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
340                                 return undefined;
341                         }
342
343                         // Clean up in case it is reused
344                         event.result = undefined;
345                         event.target = elem;
346
347                         // Clone the incoming data, if any
348                         data = jQuery.makeArray( data );
349                         data.unshift( event );
350                 }
351
352                 event.currentTarget = elem;
353
354                 // Trigger the event, it is assumed that "handle" is a function
355                 var handle = elem.nodeType ?
356                         jQuery.data( elem, "handle" ) :
357                         (jQuery.data( elem, "__events__" ) || {}).handle;
358
359                 if ( handle ) {
360                         handle.apply( elem, data );
361                 }
362
363                 var parent = elem.parentNode || elem.ownerDocument;
364
365                 // Trigger an inline bound script
366                 try {
367                         if ( !(elem && elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()]) ) {
368                                 if ( elem[ "on" + type ] && elem[ "on" + type ].apply( elem, data ) === false ) {
369                                         event.result = false;
370                                         event.preventDefault();
371                                 }
372                         }
373
374                 // prevent IE from throwing an error for some elements with some event types, see #3533
375                 } catch (inlineError) {}
376
377                 if ( !event.isPropagationStopped() && parent ) {
378                         jQuery.event.trigger( event, data, parent, true );
379
380                 } else if ( !event.isDefaultPrevented() ) {
381                         var old,
382                                 target = event.target,
383                                 targetType = type.replace( rnamespaces, "" ),
384                                 isClick = jQuery.nodeName( target, "a" ) && targetType === "click",
385                                 special = jQuery.event.special[ targetType ] || {};
386
387                         if ( (!special._default || special._default.call( elem, event ) === false) && 
388                                 !isClick && !(target && target.nodeName && jQuery.noData[target.nodeName.toLowerCase()]) ) {
389
390                                 try {
391                                         if ( target[ targetType ] ) {
392                                                 // Make sure that we don't accidentally re-trigger the onFOO events
393                                                 old = target[ "on" + targetType ];
394
395                                                 if ( old ) {
396                                                         target[ "on" + targetType ] = null;
397                                                 }
398
399                                                 jQuery.event.triggered = true;
400                                                 target[ targetType ]();
401                                         }
402
403                                 // prevent IE from throwing an error for some elements with some event types, see #3533
404                                 } catch (triggerError) {}
405
406                                 if ( old ) {
407                                         target[ "on" + targetType ] = old;
408                                 }
409
410                                 jQuery.event.triggered = false;
411                         }
412                 }
413         },
414
415         handle: function( event ) {
416                 var all, handlers, namespaces, namespace_re, events,
417                         namespace_sort = [],
418                         args = jQuery.makeArray( arguments );
419
420                 event = args[0] = jQuery.event.fix( event || window.event );
421                 event.currentTarget = this;
422
423                 // Namespaced event handlers
424                 all = event.type.indexOf(".") < 0 && !event.exclusive;
425
426                 if ( !all ) {
427                         namespaces = event.type.split(".");
428                         event.type = namespaces.shift();
429                         namespace_sort = namespaces.slice(0).sort();
430                         namespace_re = new RegExp("(^|\\.)" + namespace_sort.join("\\.(?:.*\\.)?") + "(\\.|$)");
431                 }
432
433                 event.namespace = event.namespace || namespace_sort.join(".");
434
435                 events = jQuery.data(this, this.nodeType ? "events" : "__events__");
436
437                 if ( typeof events === "function" ) {
438                         events = events.events;
439                 }
440
441                 handlers = (events || {})[ event.type ];
442
443                 if ( events && handlers ) {
444                         // Clone the handlers to prevent manipulation
445                         handlers = handlers.slice(0);
446
447                         for ( var j = 0, l = handlers.length; j < l; j++ ) {
448                                 var handleObj = handlers[ j ];
449
450                                 // Filter the functions by class
451                                 if ( all || namespace_re.test( handleObj.namespace ) ) {
452                                         // Pass in a reference to the handler function itself
453                                         // So that we can later remove it
454                                         event.handler = handleObj.handler;
455                                         event.data = handleObj.data;
456                                         event.handleObj = handleObj;
457         
458                                         var ret = handleObj.handler.apply( this, args );
459
460                                         if ( ret !== undefined ) {
461                                                 event.result = ret;
462                                                 if ( ret === false ) {
463                                                         event.preventDefault();
464                                                         event.stopPropagation();
465                                                 }
466                                         }
467
468                                         if ( event.isImmediatePropagationStopped() ) {
469                                                 break;
470                                         }
471                                 }
472                         }
473                 }
474
475                 return event.result;
476         },
477
478         props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
479
480         fix: function( event ) {
481                 if ( event[ jQuery.expando ] ) {
482                         return event;
483                 }
484
485                 // store a copy of the original event object
486                 // and "clone" to set read-only properties
487                 var originalEvent = event;
488                 event = jQuery.Event( originalEvent );
489
490                 for ( var i = this.props.length, prop; i; ) {
491                         prop = this.props[ --i ];
492                         event[ prop ] = originalEvent[ prop ];
493                 }
494
495                 // Fix target property, if necessary
496                 if ( !event.target ) {
497                         // Fixes #1925 where srcElement might not be defined either
498                         event.target = event.srcElement || document;
499                 }
500
501                 // check if target is a textnode (safari)
502                 if ( event.target.nodeType === 3 ) {
503                         event.target = event.target.parentNode;
504                 }
505
506                 // Add relatedTarget, if necessary
507                 if ( !event.relatedTarget && event.fromElement ) {
508                         event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
509                 }
510
511                 // Calculate pageX/Y if missing and clientX/Y available
512                 if ( event.pageX == null && event.clientX != null ) {
513                         var doc = document.documentElement,
514                                 body = document.body;
515
516                         event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
517                         event.pageY = event.clientY + (doc && doc.scrollTop  || body && body.scrollTop  || 0) - (doc && doc.clientTop  || body && body.clientTop  || 0);
518                 }
519
520                 // Add which for key events
521                 if ( event.which == null && (event.charCode != null || event.keyCode != null) ) {
522                         event.which = event.charCode != null ? event.charCode : event.keyCode;
523                 }
524
525                 // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
526                 if ( !event.metaKey && event.ctrlKey ) {
527                         event.metaKey = event.ctrlKey;
528                 }
529
530                 // Add which for click: 1 === left; 2 === middle; 3 === right
531                 // Note: button is not normalized, so don't use it
532                 if ( !event.which && event.button !== undefined ) {
533                         event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
534                 }
535
536                 return event;
537         },
538
539         // Deprecated, use jQuery.guid instead
540         guid: 1E8,
541
542         // Deprecated, use jQuery.proxy instead
543         proxy: jQuery.proxy,
544
545         special: {
546                 ready: {
547                         // Make sure the ready event is setup
548                         setup: jQuery.bindReady,
549                         teardown: jQuery.noop
550                 },
551
552                 live: {
553                         add: function( handleObj ) {
554                                 jQuery.event.add( this,
555                                         liveConvert( handleObj.origType, handleObj.selector ),
556                                         jQuery.extend({}, handleObj, {handler: liveHandler, guid: handleObj.handler.guid}) ); 
557                         },
558
559                         remove: function( handleObj ) {
560                                 jQuery.event.remove( this, liveConvert( handleObj.origType, handleObj.selector ), handleObj );
561                         }
562                 },
563
564                 beforeunload: {
565                         setup: function( data, namespaces, eventHandle ) {
566                                 // We only want to do this special case on windows
567                                 if ( jQuery.isWindow( this ) ) {
568                                         this.onbeforeunload = eventHandle;
569                                 }
570                         },
571
572                         teardown: function( namespaces, eventHandle ) {
573                                 if ( this.onbeforeunload === eventHandle ) {
574                                         this.onbeforeunload = null;
575                                 }
576                         }
577                 }
578         }
579 };
580
581 jQuery.removeEvent = document.removeEventListener ?
582         function( elem, type, handle ) {
583                 if ( elem.removeEventListener ) {
584                         elem.removeEventListener( type, handle, false );
585                 }
586         } : 
587         function( elem, type, handle ) {
588                 if ( elem.detachEvent ) {
589                         elem.detachEvent( "on" + type, handle );
590                 }
591         };
592
593 jQuery.Event = function( src ) {
594         // Allow instantiation without the 'new' keyword
595         if ( !this.preventDefault ) {
596                 return new jQuery.Event( src );
597         }
598
599         // Event object
600         if ( src && src.type ) {
601                 this.originalEvent = src;
602                 this.type = src.type;
603         // Event type
604         } else {
605                 this.type = src;
606         }
607
608         // timeStamp is buggy for some events on Firefox(#3843)
609         // So we won't rely on the native value
610         this.timeStamp = jQuery.now();
611
612         // Mark it as fixed
613         this[ jQuery.expando ] = true;
614 };
615
616 function returnFalse() {
617         return false;
618 }
619 function returnTrue() {
620         return true;
621 }
622
623 // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
624 // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
625 jQuery.Event.prototype = {
626         preventDefault: function() {
627                 this.isDefaultPrevented = returnTrue;
628
629                 var e = this.originalEvent;
630                 if ( !e ) {
631                         return;
632                 }
633                 
634                 // if preventDefault exists run it on the original event
635                 if ( e.preventDefault ) {
636                         e.preventDefault();
637
638                 // otherwise set the returnValue property of the original event to false (IE)
639                 } else {
640                         e.returnValue = false;
641                 }
642         },
643         stopPropagation: function() {
644                 this.isPropagationStopped = returnTrue;
645
646                 var e = this.originalEvent;
647                 if ( !e ) {
648                         return;
649                 }
650                 // if stopPropagation exists run it on the original event
651                 if ( e.stopPropagation ) {
652                         e.stopPropagation();
653                 }
654                 // otherwise set the cancelBubble property of the original event to true (IE)
655                 e.cancelBubble = true;
656         },
657         stopImmediatePropagation: function() {
658                 this.isImmediatePropagationStopped = returnTrue;
659                 this.stopPropagation();
660         },
661         isDefaultPrevented: returnFalse,
662         isPropagationStopped: returnFalse,
663         isImmediatePropagationStopped: returnFalse
664 };
665
666 // Checks if an event happened on an element within another element
667 // Used in jQuery.event.special.mouseenter and mouseleave handlers
668 var withinElement = function( event ) {
669         // Check if mouse(over|out) are still within the same parent element
670         var parent = event.relatedTarget;
671
672         // Firefox sometimes assigns relatedTarget a XUL element
673         // which we cannot access the parentNode property of
674         try {
675                 // Traverse up the tree
676                 while ( parent && parent !== this ) {
677                         parent = parent.parentNode;
678                 }
679
680                 if ( parent !== this ) {
681                         // set the correct event type
682                         event.type = event.data;
683
684                         // handle event if we actually just moused on to a non sub-element
685                         jQuery.event.handle.apply( this, arguments );
686                 }
687
688         // assuming we've left the element since we most likely mousedover a xul element
689         } catch(e) { }
690 },
691
692 // In case of event delegation, we only need to rename the event.type,
693 // liveHandler will take care of the rest.
694 delegate = function( event ) {
695         event.type = event.data;
696         jQuery.event.handle.apply( this, arguments );
697 };
698
699 // Create mouseenter and mouseleave events
700 jQuery.each({
701         mouseenter: "mouseover",
702         mouseleave: "mouseout"
703 }, function( orig, fix ) {
704         jQuery.event.special[ orig ] = {
705                 setup: function( data ) {
706                         jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig );
707                 },
708                 teardown: function( data ) {
709                         jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement );
710                 }
711         };
712 });
713
714 // submit delegation
715 if ( !jQuery.support.submitBubbles ) {
716
717         jQuery.event.special.submit = {
718                 setup: function( data, namespaces ) {
719                         if ( this.nodeName && this.nodeName.toLowerCase() !== "form" ) {
720                                 jQuery.event.add(this, "click.specialSubmit", function( e ) {
721                                         var elem = e.target,
722                                                 type = elem.type;
723
724                                         if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
725                                                 e.liveFired = undefined;
726                                                 return trigger( "submit", this, arguments );
727                                         }
728                                 });
729          
730                                 jQuery.event.add(this, "keypress.specialSubmit", function( e ) {
731                                         var elem = e.target,
732                                                 type = elem.type;
733
734                                         if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
735                                                 e.liveFired = undefined;
736                                                 return trigger( "submit", this, arguments );
737                                         }
738                                 });
739
740                         } else {
741                                 return false;
742                         }
743                 },
744
745                 teardown: function( namespaces ) {
746                         jQuery.event.remove( this, ".specialSubmit" );
747                 }
748         };
749
750 }
751
752 // change delegation, happens here so we have bind.
753 if ( !jQuery.support.changeBubbles ) {
754
755         var changeFilters,
756
757         getVal = function( elem ) {
758                 var type = elem.type, val = elem.value;
759
760                 if ( type === "radio" || type === "checkbox" ) {
761                         val = elem.checked;
762
763                 } else if ( type === "select-multiple" ) {
764                         val = elem.selectedIndex > -1 ?
765                                 jQuery.map( elem.options, function( elem ) {
766                                         return elem.selected;
767                                 }).join("-") :
768                                 "";
769
770                 } else if ( elem.nodeName.toLowerCase() === "select" ) {
771                         val = elem.selectedIndex;
772                 }
773
774                 return val;
775         },
776
777         testChange = function testChange( e ) {
778                 var elem = e.target, data, val;
779
780                 if ( !rformElems.test( elem.nodeName ) || elem.readOnly ) {
781                         return;
782                 }
783
784                 data = jQuery.data( elem, "_change_data" );
785                 val = getVal(elem);
786
787                 // the current data will be also retrieved by beforeactivate
788                 if ( e.type !== "focusout" || elem.type !== "radio" ) {
789                         jQuery.data( elem, "_change_data", val );
790                 }
791                 
792                 if ( data === undefined || val === data ) {
793                         return;
794                 }
795
796                 if ( data != null || val ) {
797                         e.type = "change";
798                         e.liveFired = undefined;
799                         return jQuery.event.trigger( e, arguments[1], elem );
800                 }
801         };
802
803         jQuery.event.special.change = {
804                 filters: {
805                         focusout: testChange, 
806
807                         beforedeactivate: testChange,
808
809                         click: function( e ) {
810                                 var elem = e.target, type = elem.type;
811
812                                 if ( type === "radio" || type === "checkbox" || elem.nodeName.toLowerCase() === "select" ) {
813                                         return testChange.call( this, e );
814                                 }
815                         },
816
817                         // Change has to be called before submit
818                         // Keydown will be called before keypress, which is used in submit-event delegation
819                         keydown: function( e ) {
820                                 var elem = e.target, type = elem.type;
821
822                                 if ( (e.keyCode === 13 && elem.nodeName.toLowerCase() !== "textarea") ||
823                                         (e.keyCode === 32 && (type === "checkbox" || type === "radio")) ||
824                                         type === "select-multiple" ) {
825                                         return testChange.call( this, e );
826                                 }
827                         },
828
829                         // Beforeactivate happens also before the previous element is blurred
830                         // with this event you can't trigger a change event, but you can store
831                         // information
832                         beforeactivate: function( e ) {
833                                 var elem = e.target;
834                                 jQuery.data( elem, "_change_data", getVal(elem) );
835                         }
836                 },
837
838                 setup: function( data, namespaces ) {
839                         if ( this.type === "file" ) {
840                                 return false;
841                         }
842
843                         for ( var type in changeFilters ) {
844                                 jQuery.event.add( this, type + ".specialChange", changeFilters[type] );
845                         }
846
847                         return rformElems.test( this.nodeName );
848                 },
849
850                 teardown: function( namespaces ) {
851                         jQuery.event.remove( this, ".specialChange" );
852
853                         return rformElems.test( this.nodeName );
854                 }
855         };
856
857         changeFilters = jQuery.event.special.change.filters;
858
859         // Handle when the input is .focus()'d
860         changeFilters.focus = changeFilters.beforeactivate;
861 }
862
863 function trigger( type, elem, args ) {
864         args[0].type = type;
865         return jQuery.event.handle.apply( elem, args );
866 }
867
868 // Create "bubbling" focus and blur events
869 if ( document.addEventListener ) {
870         jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
871                 jQuery.event.special[ fix ] = {
872                         setup: function() {
873                                 if ( focusCounts[fix]++ === 0 ) {
874                                         document.addEventListener( orig, handler, true );
875                                 }
876                         }, 
877                         teardown: function() { 
878                                 if ( --focusCounts[fix] === 0 ) {
879                                         document.removeEventListener( orig, handler, true );
880                                 }
881                         }
882                 };
883
884                 function handler( e ) { 
885                         e = jQuery.event.fix( e );
886                         e.type = fix;
887                         return jQuery.event.trigger( e, null, e.target );
888                 }
889         });
890 }
891
892 jQuery.each(["bind", "one"], function( i, name ) {
893         jQuery.fn[ name ] = function( type, data, fn ) {
894                 // Handle object literals
895                 if ( typeof type === "object" ) {
896                         for ( var key in type ) {
897                                 this[ name ](key, data, type[key], fn);
898                         }
899                         return this;
900                 }
901                 
902                 if ( jQuery.isFunction( data ) || data === false ) {
903                         fn = data;
904                         data = undefined;
905                 }
906
907                 var handler = name === "one" ? jQuery.proxy( fn, function( event ) {
908                         jQuery( this ).unbind( event, handler );
909                         return fn.apply( this, arguments );
910                 }) : fn;
911
912                 if ( type === "unload" && name !== "one" ) {
913                         this.one( type, data, fn );
914
915                 } else {
916                         for ( var i = 0, l = this.length; i < l; i++ ) {
917                                 jQuery.event.add( this[i], type, handler, data );
918                         }
919                 }
920
921                 return this;
922         };
923 });
924
925 jQuery.fn.extend({
926         unbind: function( type, fn ) {
927                 // Handle object literals
928                 if ( typeof type === "object" && !type.preventDefault ) {
929                         for ( var key in type ) {
930                                 this.unbind(key, type[key]);
931                         }
932
933                 } else {
934                         for ( var i = 0, l = this.length; i < l; i++ ) {
935                                 jQuery.event.remove( this[i], type, fn );
936                         }
937                 }
938
939                 return this;
940         },
941         
942         delegate: function( selector, types, data, fn ) {
943                 return this.live( types, data, fn, selector );
944         },
945         
946         undelegate: function( selector, types, fn ) {
947                 if ( arguments.length === 0 ) {
948                                 return this.unbind( "live" );
949                 
950                 } else {
951                         return this.die( types, null, fn, selector );
952                 }
953         },
954         
955         trigger: function( type, data ) {
956                 return this.each(function() {
957                         jQuery.event.trigger( type, data, this );
958                 });
959         },
960
961         triggerHandler: function( type, data ) {
962                 if ( this[0] ) {
963                         var event = jQuery.Event( type );
964                         event.preventDefault();
965                         event.stopPropagation();
966                         jQuery.event.trigger( event, data, this[0] );
967                         return event.result;
968                 }
969         },
970
971         toggle: function( fn ) {
972                 // Save reference to arguments for access in closure
973                 var args = arguments,
974                         i = 1;
975
976                 // link all the functions, so any of them can unbind this click handler
977                 while ( i < args.length ) {
978                         jQuery.proxy( fn, args[ i++ ] );
979                 }
980
981                 return this.click( jQuery.proxy( fn, function( event ) {
982                         // Figure out which function to execute
983                         var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i;
984                         jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 );
985
986                         // Make sure that clicks stop
987                         event.preventDefault();
988
989                         // and execute the function
990                         return args[ lastToggle ].apply( this, arguments ) || false;
991                 }));
992         },
993
994         hover: function( fnOver, fnOut ) {
995                 return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
996         }
997 });
998
999 var liveMap = {
1000         focus: "focusin",
1001         blur: "focusout",
1002         mouseenter: "mouseover",
1003         mouseleave: "mouseout"
1004 };
1005
1006 jQuery.each(["live", "die"], function( i, name ) {
1007         jQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) {
1008                 var type, i = 0, match, namespaces, preType,
1009                         selector = origSelector || this.selector,
1010                         context = origSelector ? this : jQuery( this.context );
1011                 
1012                 if ( typeof types === "object" && !types.preventDefault ) {
1013                         for ( var key in types ) {
1014                                 context[ name ]( key, data, types[key], selector );
1015                         }
1016                         
1017                         return this;
1018                 }
1019
1020                 if ( jQuery.isFunction( data ) ) {
1021                         fn = data;
1022                         data = undefined;
1023                 }
1024
1025                 types = (types || "").split(" ");
1026
1027                 while ( (type = types[ i++ ]) != null ) {
1028                         match = rnamespaces.exec( type );
1029                         namespaces = "";
1030
1031                         if ( match )  {
1032                                 namespaces = match[0];
1033                                 type = type.replace( rnamespaces, "" );
1034                         }
1035
1036                         if ( type === "hover" ) {
1037                                 types.push( "mouseenter" + namespaces, "mouseleave" + namespaces );
1038                                 continue;
1039                         }
1040
1041                         preType = type;
1042
1043                         if ( type === "focus" || type === "blur" ) {
1044                                 types.push( liveMap[ type ] + namespaces );
1045                                 type = type + namespaces;
1046
1047                         } else {
1048                                 type = (liveMap[ type ] || type) + namespaces;
1049                         }
1050
1051                         if ( name === "live" ) {
1052                                 // bind live handler
1053                                 for ( var j = 0, l = context.length; j < l; j++ ) {
1054                                         jQuery.event.add( context[j], "live." + liveConvert( type, selector ),
1055                                                 { data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } );
1056                                 }
1057
1058                         } else {
1059                                 // unbind live handler
1060                                 context.unbind( "live." + liveConvert( type, selector ), fn );
1061                         }
1062                 }
1063                 
1064                 return this;
1065         };
1066 });
1067
1068 function liveHandler( event ) {
1069         var stop, maxLevel, related, match, handleObj, elem, j, i, l, data, close, namespace, ret,
1070                 elems = [],
1071                 selectors = [],
1072                 events = jQuery.data( this, this.nodeType ? "events" : "__events__" );
1073
1074         if ( typeof events === "function" ) {
1075                 events = events.events;
1076         }
1077
1078         // Make sure we avoid non-left-click bubbling in Firefox (#3861) and disabled elements in IE (#6911)
1079         if ( event.liveFired === this || !events || !events.live || event.target.disabled || event.button && event.type === "click" ) {
1080                 return;
1081         }
1082         
1083         if ( event.namespace ) {
1084                 namespace = new RegExp("(^|\\.)" + event.namespace.split(".").join("\\.(?:.*\\.)?") + "(\\.|$)");
1085         }
1086
1087         event.liveFired = this;
1088
1089         var live = events.live.slice(0);
1090
1091         for ( j = 0; j < live.length; j++ ) {
1092                 handleObj = live[j];
1093
1094                 if ( handleObj.origType.replace( rnamespaces, "" ) === event.type ) {
1095                         selectors.push( handleObj.selector );
1096
1097                 } else {
1098                         live.splice( j--, 1 );
1099                 }
1100         }
1101
1102         match = jQuery( event.target ).closest( selectors, event.currentTarget );
1103
1104         for ( i = 0, l = match.length; i < l; i++ ) {
1105                 close = match[i];
1106
1107                 for ( j = 0; j < live.length; j++ ) {
1108                         handleObj = live[j];
1109
1110                         if ( close.selector === handleObj.selector && (!namespace || namespace.test( handleObj.namespace )) ) {
1111                                 elem = close.elem;
1112                                 related = null;
1113
1114                                 // Those two events require additional checking
1115                                 if ( handleObj.preType === "mouseenter" || handleObj.preType === "mouseleave" ) {
1116                                         event.type = handleObj.preType;
1117                                         related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0];
1118                                 }
1119
1120                                 if ( !related || related !== elem ) {
1121                                         elems.push({ elem: elem, handleObj: handleObj, level: close.level });
1122                                 }
1123                         }
1124                 }
1125         }
1126
1127         for ( i = 0, l = elems.length; i < l; i++ ) {
1128                 match = elems[i];
1129
1130                 if ( maxLevel && match.level > maxLevel ) {
1131                         break;
1132                 }
1133
1134                 event.currentTarget = match.elem;
1135                 event.data = match.handleObj.data;
1136                 event.handleObj = match.handleObj;
1137
1138                 ret = match.handleObj.origHandler.apply( match.elem, arguments );
1139
1140                 if ( ret === false || event.isPropagationStopped() ) {
1141                         maxLevel = match.level;
1142
1143                         if ( ret === false ) {
1144                                 stop = false;
1145                         }
1146                         if ( event.isImmediatePropagationStopped() ) {
1147                                 break;
1148                         }
1149                 }
1150         }
1151
1152         return stop;
1153 }
1154
1155 function liveConvert( type, selector ) {
1156         return (type && type !== "*" ? type + "." : "") + selector.replace(rperiod, "`").replace(rspace, "&");
1157 }
1158
1159 jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
1160         "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
1161         "change select submit keydown keypress keyup error").split(" "), function( i, name ) {
1162
1163         // Handle event binding
1164         jQuery.fn[ name ] = function( data, fn ) {
1165                 if ( fn == null ) {
1166                         fn = data;
1167                         data = null;
1168                 }
1169
1170                 return arguments.length > 0 ?
1171                         this.bind( name, data, fn ) :
1172                         this.trigger( name );
1173         };
1174
1175         if ( jQuery.attrFn ) {
1176                 jQuery.attrFn[ name ] = true;
1177         }
1178 });
1179
1180 // Prevent memory leaks in IE
1181 // Window isn't included so as not to unbind existing unload events
1182 // More info:
1183 //  - http://isaacschlueter.com/2006/10/msie-memory-leaks/
1184 if ( window.attachEvent && !window.addEventListener ) {
1185         jQuery(window).bind("unload", function() {
1186                 for ( var id in jQuery.cache ) {
1187                         if ( jQuery.cache[ id ].handle ) {
1188                                 // Try/Catch is to handle iframes being unloaded, see #4280
1189                                 try {
1190                                         jQuery.event.remove( jQuery.cache[ id ].handle.elem );
1191                                 } catch(e) {}
1192                         }
1193                 }
1194         });
1195 }
1196
1197 })( jQuery );