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 // If no handle is found then we must be trying to bind to one of the
61 // banned noData elements
66 // Add elem as a property of the handle function
67 // This is to prevent a memory leak with non-native
71 // Handle multiple events separated by a space
72 // jQuery(...).bind("mouseover mouseout", fn);
73 types = types.split( /\s+/ );
75 while ( (type = types[ i++ ]) ) {
76 // Namespaced event handlers
77 var namespaces = type.split(".");
78 type = namespaces.shift();
79 handler.type = namespaces.slice(0).sort().join(".");
81 // Get the current list of functions bound to this event
82 var handlers = events[ type ],
83 special = this.special[ type ] || {};
87 // Init the event handler queue
89 handlers = events[ type ] = {};
91 // Check for a special event handler
92 // Only use addEventListener/attachEvent if the special
93 // events handler returns false
94 if ( !special.setup || special.setup.call( elem, data, namespaces, handler) === false ) {
95 // Bind the global event handler to the element
96 if ( elem.addEventListener ) {
97 elem.addEventListener( type, handle, false );
98 } else if ( elem.attachEvent ) {
99 elem.attachEvent( "on" + type, handle );
105 var modifiedHandler = special.add.call( elem, handler, data, namespaces, handlers );
106 if ( modifiedHandler && jQuery.isFunction( modifiedHandler ) ) {
107 modifiedHandler.guid = modifiedHandler.guid || handler.guid;
108 handler = modifiedHandler;
112 // Add the function to the element's handler list
113 handlers[ handler.guid ] = handler;
115 // Keep track of which events have been used, for global triggering
116 this.global[ type ] = true;
119 // Nullify elem to prevent memory leaks in IE
125 // Detach an event or set of events from an element
126 remove: function( elem, types, handler ) {
127 // don't do events on text and comment nodes
128 if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
132 var events = jQuery.data( elem, "events" ), ret, type, fn;
135 // Unbind all events for the element
136 if ( types === undefined || (typeof types === "string" && types.charAt(0) === ".") ) {
137 for ( type in events ) {
138 this.remove( elem, type + (types || "") );
141 // types is actually an event object here
143 handler = types.handler;
147 // Handle multiple events separated by a space
148 // jQuery(...).unbind("mouseover mouseout", fn);
149 types = types.split(/\s+/);
151 while ( (type = types[ i++ ]) ) {
152 // Namespaced event handlers
153 var namespaces = type.split(".");
154 type = namespaces.shift();
155 var all = !namespaces.length,
156 cleaned = jQuery.map( namespaces.slice(0).sort(), fcleanup ),
157 namespace = new RegExp("(^|\\.)" + cleaned.join("\\.(?:.*\\.)?") + "(\\.|$)"),
158 special = this.special[ type ] || {};
160 if ( events[ type ] ) {
161 // remove the given handler for the given type
163 fn = events[ type ][ handler.guid ];
164 delete events[ type ][ handler.guid ];
166 // remove all handlers for the given type
168 for ( var handle in events[ type ] ) {
169 // Handle the removal of namespaced events
170 if ( all || namespace.test( events[ type ][ handle ].type ) ) {
171 delete events[ type ][ handle ];
176 if ( special.remove ) {
177 special.remove.call( elem, namespaces, fn);
180 // remove generic event handler if no more handlers exist
181 for ( ret in events[ type ] ) {
185 if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
186 if ( elem.removeEventListener ) {
187 elem.removeEventListener( type, jQuery.data( elem, "handle" ), false );
188 } else if ( elem.detachEvent ) {
189 elem.detachEvent( "on" + type, jQuery.data( elem, "handle" ) );
193 delete events[ type ];
199 // Remove the expando if it's no longer used
200 for ( ret in events ) {
204 var handle = jQuery.data( elem, "handle" );
208 jQuery.removeData( elem, "events" );
209 jQuery.removeData( elem, "handle" );
214 // bubbling is internal
215 trigger: function( event, data, elem /*, bubbling */ ) {
216 // Event object or event type
217 var type = event.type || event,
218 bubbling = arguments[3];
221 event = typeof event === "object" ?
222 // jQuery.Event object
223 event[expando] ? event :
225 jQuery.extend( jQuery.Event(type), event ) :
226 // Just the event type (string)
229 if ( type.indexOf("!") >= 0 ) {
230 event.type = type = type.slice(0, -1);
231 event.exclusive = true;
234 // Handle a global trigger
236 // Don't bubble custom events when global (to avoid too much overhead)
237 event.stopPropagation();
239 // Only trigger if we've ever bound an event for it
240 if ( this.global[ type ] ) {
241 jQuery.each( jQuery.cache, function() {
242 if ( this.events && this.events[type] ) {
243 jQuery.event.trigger( event, data, this.handle.elem );
249 // Handle triggering a single element
251 // don't do events on text and comment nodes
252 if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
256 // Clean up in case it is reused
257 event.result = undefined;
260 // Clone the incoming data, if any
261 data = jQuery.makeArray( data );
262 data.unshift( event );
265 event.currentTarget = elem;
267 // Trigger the event, it is assumed that "handle" is a function
268 var handle = jQuery.data( elem, "handle" );
270 handle.apply( elem, data );
273 var nativeFn, nativeHandler;
275 if ( !(elem && elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()]) ) {
276 nativeFn = elem[ type ];
277 nativeHandler = elem[ "on" + type ];
279 // prevent IE from throwing an error for some elements with some event types, see #3533
282 var isClick = jQuery.nodeName(elem, "a") && type === "click";
284 // Trigger the native events (except for clicks on links)
285 if ( !bubbling && nativeFn && !event.isDefaultPrevented() && !isClick ) {
286 this.triggered = true;
289 // prevent IE from throwing an error for some hidden elements
292 // Handle triggering native .onfoo handlers
293 } else if ( nativeHandler && elem[ "on" + type ].apply( elem, data ) === false ) {
294 event.result = false;
297 this.triggered = false;
299 if ( !event.isPropagationStopped() ) {
300 var parent = elem.parentNode || elem.ownerDocument;
302 jQuery.event.trigger( event, data, parent, true );
307 handle: function( event ) {
308 // returned undefined or false
311 event = arguments[0] = jQuery.event.fix( event || window.event );
312 event.currentTarget = this;
314 // Namespaced event handlers
315 var namespaces = event.type.split(".");
316 event.type = namespaces.shift();
318 // Cache this now, all = true means, any handler
319 all = !namespaces.length && !event.exclusive;
321 var namespace = new RegExp("(^|\\.)" + namespaces.slice(0).sort().join("\\.(?:.*\\.)?") + "(\\.|$)");
323 handlers = ( jQuery.data(this, "events") || {} )[ event.type ];
325 for ( var j in handlers ) {
326 var handler = handlers[ j ];
328 // Filter the functions by class
329 if ( all || namespace.test(handler.type) ) {
330 // Pass in a reference to the handler function itself
331 // So that we can later remove it
332 event.handler = handler;
333 event.data = handler.data;
335 var ret = handler.apply( this, arguments );
337 if ( ret !== undefined ) {
339 if ( ret === false ) {
340 event.preventDefault();
341 event.stopPropagation();
345 if ( event.isImmediatePropagationStopped() ) {
355 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(" "),
357 fix: function( event ) {
358 if ( event[ expando ] ) {
362 // store a copy of the original event object
363 // and "clone" to set read-only properties
364 var originalEvent = event;
365 event = jQuery.Event( originalEvent );
367 for ( var i = this.props.length, prop; i; ) {
368 prop = this.props[ --i ];
369 event[ prop ] = originalEvent[ prop ];
372 // Fix target property, if necessary
373 if ( !event.target ) {
374 event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
377 // check if target is a textnode (safari)
378 if ( event.target.nodeType === 3 ) {
379 event.target = event.target.parentNode;
382 // Add relatedTarget, if necessary
383 if ( !event.relatedTarget && event.fromElement ) {
384 event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
387 // Calculate pageX/Y if missing and clientX/Y available
388 if ( event.pageX == null && event.clientX != null ) {
389 var doc = document.documentElement, body = document.body;
390 event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
391 event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
394 // Add which for key events
395 if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) ) {
396 event.which = event.charCode || event.keyCode;
399 // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
400 if ( !event.metaKey && event.ctrlKey ) {
401 event.metaKey = event.ctrlKey;
404 // Add which for click: 1 === left; 2 === middle; 3 === right
405 // Note: button is not normalized, so don't use it
406 if ( !event.which && event.button !== undefined ) {
407 event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
413 // Deprecated, use jQuery.guid instead
416 // Deprecated, use jQuery.proxy instead
421 // Make sure the ready event is setup
422 setup: jQuery.bindReady,
423 teardown: jQuery.noop
427 add: function( proxy, data, namespaces, live ) {
428 jQuery.extend( proxy, data || {} );
430 proxy.guid += data.selector + data.live;
431 data.liveProxy = proxy;
433 jQuery.event.add( this, data.live, liveHandler, data );
437 remove: function( namespaces ) {
438 if ( namespaces.length ) {
439 var remove = 0, name = new RegExp("(^|\\.)" + namespaces[0] + "(\\.|$)");
441 jQuery.each( (jQuery.data(this, "events").live || {}), function() {
442 if ( name.test(this.type) ) {
448 jQuery.event.remove( this, namespaces[0], liveHandler );
455 setup: function( data, namespaces, fn ) {
456 // We only want to do this special case on windows
457 if ( this.setInterval ) {
458 this.onbeforeunload = fn;
463 teardown: function( namespaces, fn ) {
464 if ( this.onbeforeunload === fn ) {
465 this.onbeforeunload = null;
472 jQuery.Event = function( src ) {
473 // Allow instantiation without the 'new' keyword
474 if ( !this.preventDefault ) {
475 return new jQuery.Event( src );
479 if ( src && src.type ) {
480 this.originalEvent = src;
481 this.type = src.type;
487 // timeStamp is buggy for some events on Firefox(#3843)
488 // So we won't rely on the native value
489 this.timeStamp = now();
492 this[ expando ] = true;
495 function returnFalse() {
498 function returnTrue() {
502 // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
503 // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
504 jQuery.Event.prototype = {
505 preventDefault: function() {
506 this.isDefaultPrevented = returnTrue;
508 var e = this.originalEvent;
513 // if preventDefault exists run it on the original event
514 if ( e.preventDefault ) {
517 // otherwise set the returnValue property of the original event to false (IE)
518 e.returnValue = false;
520 stopPropagation: function() {
521 this.isPropagationStopped = returnTrue;
523 var e = this.originalEvent;
527 // if stopPropagation exists run it on the original event
528 if ( e.stopPropagation ) {
531 // otherwise set the cancelBubble property of the original event to true (IE)
532 e.cancelBubble = true;
534 stopImmediatePropagation: function() {
535 this.isImmediatePropagationStopped = returnTrue;
536 this.stopPropagation();
538 isDefaultPrevented: returnFalse,
539 isPropagationStopped: returnFalse,
540 isImmediatePropagationStopped: returnFalse
543 // Checks if an event happened on an element within another element
544 // Used in jQuery.event.special.mouseenter and mouseleave handlers
545 var withinElement = function( event ) {
546 // Check if mouse(over|out) are still within the same parent element
547 var parent = event.relatedTarget;
549 // Traverse up the tree
550 while ( parent && parent !== this ) {
551 // Firefox sometimes assigns relatedTarget a XUL element
552 // which we cannot access the parentNode property of
554 parent = parent.parentNode;
556 // assuming we've left the element since we most likely mousedover a xul element
562 if ( parent !== this ) {
563 // set the correct event type
564 event.type = event.data;
566 // handle event if we actually just moused on to a non sub-element
567 jQuery.event.handle.apply( this, arguments );
572 // In case of event delegation, we only need to rename the event.type,
573 // liveHandler will take care of the rest.
574 delegate = function( event ) {
575 event.type = event.data;
576 jQuery.event.handle.apply( this, arguments );
579 // Create mouseenter and mouseleave events
581 mouseenter: "mouseover",
582 mouseleave: "mouseout"
583 }, function( orig, fix ) {
584 jQuery.event.special[ orig ] = {
585 setup: function( data ) {
586 jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig );
588 teardown: function( data ) {
589 jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement );
595 if ( !jQuery.support.submitBubbles ) {
597 jQuery.event.special.submit = {
598 setup: function( data, namespaces, fn ) {
599 if ( this.nodeName.toLowerCase() !== "form" ) {
600 jQuery.event.add(this, "click.specialSubmit." + fn.guid, function( e ) {
601 var elem = e.target, type = elem.type;
603 if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
604 return trigger( "submit", this, arguments );
608 jQuery.event.add(this, "keypress.specialSubmit." + fn.guid, function( e ) {
609 var elem = e.target, type = elem.type;
611 if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
612 return trigger( "submit", this, arguments );
621 remove: function( namespaces, fn ) {
622 jQuery.event.remove( this, "click.specialSubmit" + (fn ? "."+fn.guid : "") );
623 jQuery.event.remove( this, "keypress.specialSubmit" + (fn ? "."+fn.guid : "") );
629 // change delegation, happens here so we have bind.
630 if ( !jQuery.support.changeBubbles ) {
632 var formElems = /textarea|input|select/i;
634 function getVal( elem ) {
635 var type = elem.type, val = elem.value;
637 if ( type === "radio" || type === "checkbox" ) {
640 } else if ( type === "select-multiple" ) {
641 val = elem.selectedIndex > -1 ?
642 jQuery.map( elem.options, function( elem ) {
643 return elem.selected;
647 } else if ( elem.nodeName.toLowerCase() === "select" ) {
648 val = elem.selectedIndex;
654 function testChange( e ) {
655 var elem = e.target, data, val;
657 if ( !formElems.test( elem.nodeName ) || elem.readOnly ) {
661 data = jQuery.data( elem, "_change_data" );
664 // the current data will be also retrieved by beforeactivate
665 if ( e.type !== "focusout" || elem.type !== "radio" ) {
666 jQuery.data( elem, "_change_data", val );
669 if ( data === undefined || val === data ) {
673 if ( data != null || val ) {
675 return jQuery.event.trigger( e, arguments[1], elem );
679 jQuery.event.special.change = {
681 focusout: testChange,
683 click: function( e ) {
684 var elem = e.target, type = elem.type;
686 if ( type === "radio" || type === "checkbox" || elem.nodeName.toLowerCase() === "select" ) {
687 return testChange.call( this, e );
691 // Change has to be called before submit
692 // Keydown will be called before keypress, which is used in submit-event delegation
693 keydown: function( e ) {
694 var elem = e.target, type = elem.type;
696 if ( (e.keyCode === 13 && elem.nodeName.toLowerCase() !== "textarea") ||
697 (e.keyCode === 32 && (type === "checkbox" || type === "radio")) ||
698 type === "select-multiple" ) {
699 return testChange.call( this, e );
703 // Beforeactivate happens also before the previous element is blurred
704 // with this event you can't trigger a change event, but you can store
705 // information/focus[in] is not needed anymore
706 beforeactivate: function( e ) {
709 if ( elem.nodeName.toLowerCase() === "input" && elem.type === "radio" ) {
710 jQuery.data( elem, "_change_data", getVal(elem) );
714 setup: function( data, namespaces, fn ) {
715 for ( var type in changeFilters ) {
716 jQuery.event.add( this, type + ".specialChange." + fn.guid, changeFilters[type] );
719 return formElems.test( this.nodeName );
721 remove: function( namespaces, fn ) {
722 for ( var type in changeFilters ) {
723 jQuery.event.remove( this, type + ".specialChange" + (fn ? "."+fn.guid : ""), changeFilters[type] );
726 return formElems.test( this.nodeName );
730 var changeFilters = jQuery.event.special.change.filters;
734 function trigger( type, elem, args ) {
736 return jQuery.event.handle.apply( elem, args );
739 // Create "bubbling" focus and blur events
740 if ( document.addEventListener ) {
741 jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
742 jQuery.event.special[ fix ] = {
744 this.addEventListener( orig, handler, true );
746 teardown: function() {
747 this.removeEventListener( orig, handler, true );
751 function handler( e ) {
752 e = jQuery.event.fix( e );
754 return jQuery.event.handle.call( this, e );
759 jQuery.each(["bind", "one"], function( i, name ) {
760 jQuery.fn[ name ] = function( type, data, fn ) {
761 // Handle object literals
762 if ( typeof type === "object" ) {
763 for ( var key in type ) {
764 this[ name ](key, data, type[key], fn);
769 if ( jQuery.isFunction( data ) ) {
774 var handler = name === "one" ? jQuery.proxy( fn, function( event ) {
775 jQuery( this ).unbind( event, handler );
776 return fn.apply( this, arguments );
779 return type === "unload" && name !== "one" ?
780 this.one( type, data, fn ) :
781 this.each(function() {
782 jQuery.event.add( this, type, handler, data );
788 unbind: function( type, fn ) {
789 // Handle object literals
790 if ( typeof type === "object" && !type.preventDefault ) {
791 for ( var key in type ) {
792 this.unbind(key, type[key]);
797 return this.each(function() {
798 jQuery.event.remove( this, type, fn );
801 trigger: function( type, data ) {
802 return this.each(function() {
803 jQuery.event.trigger( type, data, this );
807 triggerHandler: function( type, data ) {
809 var event = jQuery.Event( type );
810 event.preventDefault();
811 event.stopPropagation();
812 jQuery.event.trigger( event, data, this[0] );
817 toggle: function( fn ) {
818 // Save reference to arguments for access in closure
819 var args = arguments, i = 1;
821 // link all the functions, so any of them can unbind this click handler
822 while ( i < args.length ) {
823 jQuery.proxy( fn, args[ i++ ] );
826 return this.click( jQuery.proxy( fn, function( event ) {
827 // Figure out which function to execute
828 var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i;
829 jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 );
831 // Make sure that clicks stop
832 event.preventDefault();
834 // and execute the function
835 return args[ lastToggle ].apply( this, arguments ) || false;
839 hover: function( fnOver, fnOut ) {
840 return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
844 jQuery.each(["live", "die"], function( i, name ) {
845 jQuery.fn[ name ] = function( types, data, fn ) {
848 if ( jQuery.isFunction( data ) ) {
853 types = (types || "").split( /\s+/ );
855 while ( (type = types[ i++ ]) != null ) {
856 type = type === "focus" ? "focusin" : // focus --> focusin
857 type === "blur" ? "focusout" : // blur --> focusout
858 type === "hover" ? types.push("mouseleave") && "mouseenter" : // hover support
861 if ( name === "live" ) {
863 jQuery( this.context ).bind( liveConvert( type, this.selector ), {
864 data: data, selector: this.selector, live: type
868 // unbind live handler
869 jQuery( this.context ).unbind( liveConvert( type, this.selector ), fn ? { guid: fn.guid + this.selector + type } : null );
877 function liveHandler( event ) {
878 var stop, elems = [], selectors = [], args = arguments,
879 related, match, fn, elem, j, i, l, data,
880 live = jQuery.extend({}, jQuery.data( this, "events" ).live);
882 // Make sure we avoid non-left-click bubbling in Firefox (#3861)
883 if ( event.button && event.type === "click" ) {
889 if ( fn.live === event.type ||
890 fn.altLive && jQuery.inArray(event.type, fn.altLive) > -1 ) {
893 if ( !(data.beforeFilter && data.beforeFilter[event.type] &&
894 !data.beforeFilter[event.type](event)) ) {
895 selectors.push( fn.selector );
902 match = jQuery( event.target ).closest( selectors, event.currentTarget );
904 for ( i = 0, l = match.length; i < l; i++ ) {
907 elem = match[i].elem;
910 if ( match[i].selector === fn.selector ) {
911 // Those two events require additional checking
912 if ( fn.live === "mouseenter" || fn.live === "mouseleave" ) {
913 related = jQuery( event.relatedTarget ).closest( fn.selector )[0];
916 if ( !related || related !== elem ) {
917 elems.push({ elem: elem, fn: fn });
923 for ( i = 0, l = elems.length; i < l; i++ ) {
925 event.currentTarget = match.elem;
926 event.data = match.fn.data;
927 if ( match.fn.apply( match.elem, args ) === false ) {
936 function liveConvert( type, selector ) {
937 return "live." + (type ? type + "." : "") + selector.replace(/\./g, "`").replace(/ /g, "&");
940 jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
941 "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
942 "change select submit keydown keypress keyup error").split(" "), function( i, name ) {
944 // Handle event binding
945 jQuery.fn[ name ] = function( fn ) {
946 return fn ? this.bind( name, fn ) : this.trigger( name );
949 if ( jQuery.attrFn ) {
950 jQuery.attrFn[ name ] = true;
954 // Prevent memory leaks in IE
955 // Window isn't included so as not to unbind existing unload events
957 // - http://isaacschlueter.com/2006/10/msie-memory-leaks/
958 if ( window.attachEvent && !window.addEventListener ) {
959 window.attachEvent("onunload", function() {
960 for ( var id in jQuery.cache ) {
961 if ( jQuery.cache[ id ].handle ) {
962 // Try/Catch is to handle iframes being unloaded, see #4280
964 jQuery.event.remove( jQuery.cache[ id ].handle.elem );