1 var fcleanup = function( nm ) {
2 return nm.replace(/[^\w\s\.\|`]/g, function( ch ) {
8 * A number of helper functions used for managing events.
9 * Many of the ideas behind this code originated from
10 * Dean Edwards' addEvent library.
14 // Bind an event to an element
15 // Original by Dean Edwards
16 add: function( elem, types, handler, data ) {
17 if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
21 // For whatever reason, IE has trouble passing the window object
22 // around, causing it to be cloned in the process
23 if ( elem.setInterval && ( elem !== window && !elem.frameElement ) ) {
27 // Make sure that the function being executed has a unique ID
28 if ( !handler.guid ) {
29 handler.guid = jQuery.guid++;
32 // if data is passed, bind to handler
33 if ( data !== undefined ) {
34 // Create temporary function pointer to original handler
37 // Create unique handler function, wrapped around original handler
38 handler = jQuery.proxy( fn );
40 // Store data in unique handler
44 // Init the element's event structure
45 var events = jQuery.data( elem, "events" ) || jQuery.data( elem, "events", {} ),
46 handle = jQuery.data( elem, "handle" ), eventHandle;
49 eventHandle = function() {
50 // Handle the second event of a trigger and when
51 // an event is called after a page has unloaded
52 return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
53 jQuery.event.handle.apply( eventHandle.elem, arguments ) :
57 handle = jQuery.data( elem, "handle", eventHandle );
60 // Add elem as a property of the handle function
61 // This is to prevent a memory leak with non-native
65 // Handle multiple events separated by a space
66 // jQuery(...).bind("mouseover mouseout", fn);
67 types = types.split( /\s+/ );
69 while ( (type = types[ i++ ]) ) {
70 // Namespaced event handlers
71 var namespaces = type.split(".");
72 type = namespaces.shift();
73 handler.type = namespaces.slice(0).sort().join(".");
75 // Get the current list of functions bound to this event
76 var handlers = events[ type ],
77 special = this.special[ type ] || {};
81 // Init the event handler queue
83 handlers = events[ type ] = {};
85 // Check for a special event handler
86 // Only use addEventListener/attachEvent if the special
87 // events handler returns false
88 if ( !special.setup || special.setup.call( elem, data, namespaces, handler) === false ) {
89 // Bind the global event handler to the element
90 if ( elem.addEventListener ) {
91 elem.addEventListener( type, handle, false );
92 } else if ( elem.attachEvent ) {
93 elem.attachEvent( "on" + type, handle );
99 var modifiedHandler = special.add.call( elem, handler, data, namespaces, handlers );
100 if ( modifiedHandler && jQuery.isFunction( modifiedHandler ) ) {
101 modifiedHandler.guid = modifiedHandler.guid || handler.guid;
102 handler = modifiedHandler;
106 // Add the function to the element's handler list
107 handlers[ handler.guid ] = handler;
109 // Keep track of which events have been used, for global triggering
110 this.global[ type ] = true;
113 // Nullify elem to prevent memory leaks in IE
119 // Detach an event or set of events from an element
120 remove: function( elem, types, handler ) {
121 // don't do events on text and comment nodes
122 if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
126 var events = jQuery.data( elem, "events" ), ret, type, fn;
129 // Unbind all events for the element
130 if ( types === undefined || (typeof types === "string" && types.charAt(0) === ".") ) {
131 for ( type in events ) {
132 this.remove( elem, type + (types || "") );
135 // types is actually an event object here
137 handler = types.handler;
141 // Handle multiple events separated by a space
142 // jQuery(...).unbind("mouseover mouseout", fn);
143 types = types.split(/\s+/);
145 while ( (type = types[ i++ ]) ) {
146 // Namespaced event handlers
147 var namespaces = type.split(".");
148 type = namespaces.shift();
149 var all = !namespaces.length,
150 cleaned = jQuery.map( namespaces.slice(0).sort(), fcleanup ),
151 namespace = new RegExp("(^|\\.)" + cleaned.join("\\.(?:.*\\.)?") + "(\\.|$)"),
152 special = this.special[ type ] || {};
154 if ( events[ type ] ) {
155 // remove the given handler for the given type
157 fn = events[ type ][ handler.guid ];
158 delete events[ type ][ handler.guid ];
160 // remove all handlers for the given type
162 for ( var handle in events[ type ] ) {
163 // Handle the removal of namespaced events
164 if ( all || namespace.test( events[ type ][ handle ].type ) ) {
165 delete events[ type ][ handle ];
170 if ( special.remove ) {
171 special.remove.call( elem, namespaces, fn);
174 // remove generic event handler if no more handlers exist
175 for ( ret in events[ type ] ) {
179 if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
180 if ( elem.removeEventListener ) {
181 elem.removeEventListener( type, jQuery.data( elem, "handle" ), false );
182 } else if ( elem.detachEvent ) {
183 elem.detachEvent( "on" + type, jQuery.data( elem, "handle" ) );
187 delete events[ type ];
193 // Remove the expando if it's no longer used
194 for ( ret in events ) {
198 var handle = jQuery.data( elem, "handle" );
202 jQuery.removeData( elem, "events" );
203 jQuery.removeData( elem, "handle" );
208 // bubbling is internal
209 trigger: function( event, data, elem /*, bubbling */ ) {
210 // Event object or event type
211 var type = event.type || event,
212 bubbling = arguments[3];
215 event = typeof event === "object" ?
216 // jQuery.Event object
217 event[expando] ? event :
219 jQuery.extend( jQuery.Event(type), event ) :
220 // Just the event type (string)
223 if ( type.indexOf("!") >= 0 ) {
224 event.type = type = type.slice(0, -1);
225 event.exclusive = true;
228 // Handle a global trigger
230 // Don't bubble custom events when global (to avoid too much overhead)
231 event.stopPropagation();
233 // Only trigger if we've ever bound an event for it
234 if ( this.global[ type ] ) {
235 jQuery.each( jQuery.cache, function() {
236 if ( this.events && this.events[type] ) {
237 jQuery.event.trigger( event, data, this.handle.elem );
243 // Handle triggering a single element
245 // don't do events on text and comment nodes
246 if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
250 // Clean up in case it is reused
251 event.result = undefined;
254 // Clone the incoming data, if any
255 data = jQuery.makeArray( data );
256 data.unshift( event );
259 event.currentTarget = elem;
261 // Trigger the event, it is assumed that "handle" is a function
262 var handle = jQuery.data( elem, "handle" );
264 handle.apply( elem, data );
267 var nativeFn, nativeHandler;
269 if ( !(elem && elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()]) ) {
270 nativeFn = elem[ type ];
271 nativeHandler = elem[ "on" + type ];
273 // prevent IE from throwing an error for some elements with some event types, see #3533
276 var isClick = jQuery.nodeName(elem, "a") && type === "click";
278 // Trigger the native events (except for clicks on links)
279 if ( !bubbling && nativeFn && !event.isDefaultPrevented() && !isClick ) {
280 this.triggered = true;
283 // prevent IE from throwing an error for some hidden elements
286 // Handle triggering native .onfoo handlers
287 } else if ( nativeHandler && elem[ "on" + type ].apply( elem, data ) === false ) {
288 event.result = false;
291 this.triggered = false;
293 if ( !event.isPropagationStopped() ) {
294 var parent = elem.parentNode || elem.ownerDocument;
296 jQuery.event.trigger( event, data, parent, true );
301 handle: function( event ) {
302 // returned undefined or false
305 event = arguments[0] = jQuery.event.fix( event || window.event );
306 event.currentTarget = this;
308 // Namespaced event handlers
309 var namespaces = event.type.split(".");
310 event.type = namespaces.shift();
312 // Cache this now, all = true means, any handler
313 all = !namespaces.length && !event.exclusive;
315 var namespace = new RegExp("(^|\\.)" + namespaces.slice(0).sort().join("\\.(?:.*\\.)?") + "(\\.|$)");
317 handlers = ( jQuery.data(this, "events") || {} )[ event.type ];
319 for ( var j in handlers ) {
320 var handler = handlers[ j ];
322 // Filter the functions by class
323 if ( all || namespace.test(handler.type) ) {
324 // Pass in a reference to the handler function itself
325 // So that we can later remove it
326 event.handler = handler;
327 event.data = handler.data;
329 var ret = handler.apply( this, arguments );
331 if ( ret !== undefined ) {
333 if ( ret === false ) {
334 event.preventDefault();
335 event.stopPropagation();
339 if ( event.isImmediatePropagationStopped() ) {
349 props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
351 fix: function( event ) {
352 if ( event[ expando ] ) {
356 // store a copy of the original event object
357 // and "clone" to set read-only properties
358 var originalEvent = event;
359 event = jQuery.Event( originalEvent );
361 for ( var i = this.props.length, prop; i; ) {
362 prop = this.props[ --i ];
363 event[ prop ] = originalEvent[ prop ];
366 // Fix target property, if necessary
367 if ( !event.target ) {
368 event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
371 // check if target is a textnode (safari)
372 if ( event.target.nodeType === 3 ) {
373 event.target = event.target.parentNode;
376 // Add relatedTarget, if necessary
377 if ( !event.relatedTarget && event.fromElement ) {
378 event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
381 // Calculate pageX/Y if missing and clientX/Y available
382 if ( event.pageX == null && event.clientX != null ) {
383 var doc = document.documentElement, body = document.body;
384 event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
385 event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
388 // Add which for key events
389 if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) ) {
390 event.which = event.charCode || event.keyCode;
393 // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
394 if ( !event.metaKey && event.ctrlKey ) {
395 event.metaKey = event.ctrlKey;
398 // Add which for click: 1 === left; 2 === middle; 3 === right
399 // Note: button is not normalized, so don't use it
400 if ( !event.which && event.button !== undefined ) {
401 event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
407 // Deprecated, use jQuery.guid instead
410 // Deprecated, use jQuery.proxy instead
415 // Make sure the ready event is setup
416 setup: jQuery.bindReady,
417 teardown: jQuery.noop
421 add: function( proxy, data, namespaces, live ) {
422 jQuery.extend( proxy, data || {} );
424 proxy.guid += data.selector + data.live;
425 jQuery.event.add( this, data.live, liveHandler, data );
429 remove: function( namespaces ) {
430 if ( namespaces.length ) {
431 var remove = 0, name = new RegExp("(^|\\.)" + namespaces[0] + "(\\.|$)");
433 jQuery.each( (jQuery.data(this, "events").live || {}), function() {
434 if ( name.test(this.type) ) {
440 jQuery.event.remove( this, namespaces[0], liveHandler );
447 setup: function( data, namespaces, fn ) {
448 // We only want to do this special case on windows
449 if ( this.setInterval ) {
450 this.onbeforeunload = fn;
455 teardown: function( namespaces, fn ) {
456 if ( this.onbeforeunload === fn ) {
457 this.onbeforeunload = null;
464 jQuery.Event = function( src ) {
465 // Allow instantiation without the 'new' keyword
466 if ( !this.preventDefault ) {
467 return new jQuery.Event( src );
471 if ( src && src.type ) {
472 this.originalEvent = src;
473 this.type = src.type;
479 // timeStamp is buggy for some events on Firefox(#3843)
480 // So we won't rely on the native value
481 this.timeStamp = now();
484 this[ expando ] = true;
487 function returnFalse() {
490 function returnTrue() {
494 // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
495 // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
496 jQuery.Event.prototype = {
497 preventDefault: function() {
498 this.isDefaultPrevented = returnTrue;
500 var e = this.originalEvent;
505 // if preventDefault exists run it on the original event
506 if ( e.preventDefault ) {
509 // otherwise set the returnValue property of the original event to false (IE)
510 e.returnValue = false;
512 stopPropagation: function() {
513 this.isPropagationStopped = returnTrue;
515 var e = this.originalEvent;
519 // if stopPropagation exists run it on the original event
520 if ( e.stopPropagation ) {
523 // otherwise set the cancelBubble property of the original event to true (IE)
524 e.cancelBubble = true;
526 stopImmediatePropagation: function() {
527 this.isImmediatePropagationStopped = returnTrue;
528 this.stopPropagation();
530 isDefaultPrevented: returnFalse,
531 isPropagationStopped: returnFalse,
532 isImmediatePropagationStopped: returnFalse
535 // Checks if an event happened on an element within another element
536 // Used in jQuery.event.special.mouseenter and mouseleave handlers
537 var withinElement = function( event ) {
538 // Check if mouse(over|out) are still within the same parent element
539 var parent = event.relatedTarget;
541 // Traverse up the tree
542 while ( parent && parent !== this ) {
543 // Firefox sometimes assigns relatedTarget a XUL element
544 // which we cannot access the parentNode property of
546 parent = parent.parentNode;
548 // assuming we've left the element since we most likely mousedover a xul element
554 if ( parent !== this ) {
555 // set the correct event type
556 event.type = event.data;
558 // handle event if we actually just moused on to a non sub-element
559 jQuery.event.handle.apply( this, arguments );
564 // In case of event delegation, we only need to rename the event.type,
565 // liveHandler will take care of the rest.
566 delegate = function( event ) {
567 event.type = event.data;
568 jQuery.event.handle.apply( this, arguments );
571 // Create mouseenter and mouseleave events
573 mouseenter: "mouseover",
574 mouseleave: "mouseout"
575 }, function( orig, fix ) {
576 jQuery.event.special[ orig ] = {
577 setup: function( data ) {
578 jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig );
580 teardown: function( data ) {
581 jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement );
587 if ( !jQuery.support.submitBubbles ) {
589 jQuery.event.special.submit = {
590 setup: function( data, namespaces, fn ) {
591 if ( this.nodeName.toLowerCase() !== "form" ) {
592 jQuery.event.add(this, "click.specialSubmit." + fn.guid, function( e ) {
593 var elem = e.target, type = elem.type;
595 if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
596 return trigger( "submit", this, arguments );
600 jQuery.event.add(this, "keypress.specialSubmit." + fn.guid, function( e ) {
601 var elem = e.target, type = elem.type;
603 if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
604 return trigger( "submit", this, arguments );
613 remove: function( namespaces, fn ) {
614 jQuery.event.remove( this, "click.specialSubmit" + (fn ? "."+fn.guid : "") );
615 jQuery.event.remove( this, "keypress.specialSubmit" + (fn ? "."+fn.guid : "") );
621 // change delegation, happens here so we have bind.
622 if ( !jQuery.support.changeBubbles ) {
624 var formElems = /textarea|input|select/i;
626 function getVal( elem ) {
627 var type = elem.type, val = elem.value;
629 if ( type === "radio" || type === "checkbox" ) {
632 } else if ( type === "select-multiple" ) {
633 val = elem.selectedIndex > -1 ?
634 jQuery.map( elem.options, function( elem ) {
635 return elem.selected;
639 } else if ( elem.nodeName.toLowerCase() === "select" ) {
640 val = elem.selectedIndex;
646 function testChange( e ) {
647 var elem = e.target, data, val;
649 if ( !formElems.test( elem.nodeName ) || elem.readOnly ) {
653 data = jQuery.data( elem, "_change_data" );
656 if ( val === data ) {
660 // the current data will be also retrieved by beforeactivate
661 if ( e.type !== "focusout" || elem.type !== "radio" ) {
662 jQuery.data( elem, "_change_data", val );
665 if ( elem.type !== "select" && (data != null || val) ) {
667 return jQuery.event.trigger( e, arguments[1], this );
671 jQuery.event.special.change = {
673 focusout: testChange,
675 click: function( e ) {
676 var elem = e.target, type = elem.type;
678 if ( type === "radio" || type === "checkbox" || elem.nodeName.toLowerCase() === "select" ) {
679 return testChange.call( this, e );
683 // Change has to be called before submit
684 // Keydown will be called before keypress, which is used in submit-event delegation
685 keydown: function( e ) {
686 var elem = e.target, type = elem.type;
688 if ( (e.keyCode === 13 && elem.nodeName.toLowerCase() !== "textarea") ||
689 (e.keyCode === 32 && (type === "checkbox" || type === "radio")) ||
690 type === "select-multiple" ) {
691 return testChange.call( this, e );
695 // Beforeactivate happens also before the previous element is blurred
696 // with this event you can't trigger a change event, but you can store
697 // information/focus[in] is not needed anymore
698 beforeactivate: function( e ) {
701 if ( elem.nodeName.toLowerCase() === "input" && elem.type === "radio" ) {
702 jQuery.data( elem, "_change_data", getVal(elem) );
706 setup: function( data, namespaces, fn ) {
707 for ( var type in changeFilters ) {
708 jQuery.event.add( this, type + ".specialChange." + fn.guid, changeFilters[type] );
711 return formElems.test( this.nodeName );
713 remove: function( namespaces, fn ) {
714 for ( var type in changeFilters ) {
715 jQuery.event.remove( this, type + ".specialChange" + (fn ? "."+fn.guid : ""), changeFilters[type] );
718 return formElems.test( this.nodeName );
722 var changeFilters = jQuery.event.special.change.filters;
726 function trigger( type, elem, args ) {
728 return jQuery.event.handle.apply( elem, args );
731 // Create "bubbling" focus and blur events
732 if ( document.addEventListener ) {
733 jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
734 jQuery.event.special[ fix ] = {
736 this.addEventListener( orig, handler, true );
738 teardown: function() {
739 this.removeEventListener( orig, handler, true );
743 function handler( e ) {
744 e = jQuery.event.fix( e );
746 return jQuery.event.handle.call( this, e );
751 jQuery.each(["bind", "one"], function( i, name ) {
752 jQuery.fn[ name ] = function( type, data, fn ) {
753 // Handle object literals
754 if ( typeof type === "object" ) {
755 for ( var key in type ) {
756 this[ name ](key, data, type[key], fn);
761 if ( jQuery.isFunction( data ) ) {
767 var handler = name === "one" ? jQuery.proxy( fn, function( event ) {
768 jQuery( this ).unbind( event, handler );
769 return fn.apply( this, arguments );
772 return type === "unload" && name !== "one" ?
773 this.one( type, data, fn, thisObject ) :
774 this.each(function() {
775 jQuery.event.add( this, type, handler, data );
781 unbind: function( type, fn ) {
782 // Handle object literals
783 if ( typeof type === "object" && !type.preventDefault ) {
784 for ( var key in type ) {
785 this.unbind(key, type[key]);
790 return this.each(function() {
791 jQuery.event.remove( this, type, fn );
794 trigger: function( type, data ) {
795 return this.each(function() {
796 jQuery.event.trigger( type, data, this );
800 triggerHandler: function( type, data ) {
802 var event = jQuery.Event( type );
803 event.preventDefault();
804 event.stopPropagation();
805 jQuery.event.trigger( event, data, this[0] );
810 toggle: function( fn ) {
811 // Save reference to arguments for access in closure
812 var args = arguments, i = 1;
814 // link all the functions, so any of them can unbind this click handler
815 while ( i < args.length ) {
816 jQuery.proxy( fn, args[ i++ ] );
819 return this.click( jQuery.proxy( fn, function( event ) {
820 // Figure out which function to execute
821 var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i;
822 jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 );
824 // Make sure that clicks stop
825 event.preventDefault();
827 // and execute the function
828 return args[ lastToggle ].apply( this, arguments ) || false;
832 hover: function( fnOver, fnOut ) {
833 return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
836 live: function( type, data, fn ) {
837 if ( jQuery.isFunction( data ) ) {
842 jQuery( this.context ).bind( liveConvert( type, this.selector ), {
843 data: data, selector: this.selector, live: type
849 die: function( type, fn ) {
850 jQuery( this.context ).unbind( liveConvert( type, this.selector ), fn ? { guid: fn.guid + this.selector + type } : null );
855 function liveHandler( event ) {
856 var stop = true, elems = [], selectors = [], args = arguments,
857 related, match, fn, elem, j, i, data,
858 live = jQuery.extend({}, jQuery.data( this, "events" ).live);
862 if ( fn.live === event.type ||
863 fn.altLive && jQuery.inArray(event.type, fn.altLive) > -1 ) {
866 if ( !(data.beforeFilter && data.beforeFilter[event.type] &&
867 !data.beforeFilter[event.type](event)) ) {
868 selectors.push( fn.selector );
875 match = jQuery( event.target ).closest( selectors, event.currentTarget );
877 for ( i = 0, l = match.length; i < l; i++ ) {
880 elem = match[i].elem;
883 if ( match[i].selector === fn.selector ) {
884 // Those two events require additional checking
885 if ( fn.live === "mouseenter" || fn.live === "mouseleave" ) {
886 related = jQuery( event.relatedTarget ).closest( fn.selector )[0];
889 if ( !related || related !== elem ) {
890 elems.push({ elem: elem, fn: fn });
896 for ( i = 0, l = elems.length; i < l; i++ ) {
898 event.currentTarget = match.elem;
899 event.data = match.fn.data;
900 if ( match.fn.apply( match.elem, args ) === false ) {
909 function liveConvert( type, selector ) {
910 return ["live", type, selector.replace(/\./g, "`").replace(/ /g, "&")].join(".");
913 jQuery.each( ("blur focus load resize scroll unload click dblclick " +
914 "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
915 "change select submit keydown keypress keyup error").split(" "), function( i, name ) {
917 // Handle event binding
918 jQuery.fn[ name ] = function( fn ) {
919 return fn ? this.bind( name, fn ) : this.trigger( name );
922 if ( jQuery.fnAttr ) {
923 jQuery.fnAttr[ name ] = true;
927 // Prevent memory leaks in IE
928 // Window isn't included so as not to unbind existing unload events
930 // - http://isaacschlueter.com/2006/10/msie-memory-leaks/
931 if ( window.attachEvent && !window.addEventListener ) {
932 window.attachEvent("onunload", function() {
933 for ( var id in jQuery.cache ) {
934 if ( jQuery.cache[ id ].handle ) {
935 // Try/Catch is to handle iframes being unloaded, see #4280
937 jQuery.event.remove( jQuery.cache[ id ].handle.elem );