var fn = handler;
// Create unique handler function, wrapped around original handler
- handler = this.proxy( fn, function() {
- // Pass arguments and context to original handler
- return fn.apply(this, arguments);
- });
+ handler = this.proxy( fn );
// Store data in unique handler
handler.data = data;
// Get the current list of functions bound to this event
var handlers = events[type];
-
+
if ( jQuery.event.specialAll[type] )
jQuery.event.specialAll[type].setup.call(elem, data, namespaces);
// Namespaced event handlers
var namespaces = type.split(".");
type = namespaces.shift();
- var namespace = RegExp("(^|\\.)" + namespaces.slice().sort().join(".*\\.") + "(\\.|$)");
+ var namespace = new RegExp("(^|\\.)" + namespaces.slice().sort().join(".*\\.") + "(\\.|$)");
if ( events[type] ) {
// remove the given handler for the given type
// remove all handlers for the given type
else
- for ( handler in events[type] )
+ for ( var handle in events[type] )
// Handle the removal of namespaced events
- if ( namespace.test(events[type][handler].type) )
- delete events[type][handler];
-
+ if ( namespace.test(events[type][handle].type) )
+ delete events[type][handle];
+
if ( jQuery.event.specialAll[type] )
jQuery.event.specialAll[type].teardown.call(elem, namespaces);
}
},
- trigger: function( event, data, elem, extra) {
+ // bubbling is internal
+ trigger: function( event, data, elem, bubbling ) {
// Event object or event type
var type = event.type || event;
- event = typeof event === "object" ?
- // jQuery.Event object
- event[expando] ? event :
- // Object literal
- jQuery.extend( jQuery.Event(type), event ) :
- // Just the event type (string)
- jQuery.Event(type);
-
- if ( type.indexOf("!") >= 0 ) {
- event.type = type = type.slice(0, -1);
- event.exclusive = true;
- }
-
- // Handle a global trigger
- if ( !elem ) {
- // Don't bubble custom events when global (to avoid too much overhead)
- event.stopPropagation();
- // Only trigger if we've ever bound an event for it
- if ( this.global[type] )
- jQuery.each( jQuery.cache, function(){
- if ( this.events && this.events[type] )
- jQuery.event.trigger( event, data, this.handle.elem );
- });
+ if( !bubbling ){
+ event = typeof event === "object" ?
+ // jQuery.Event object
+ event[expando] ? event :
+ // Object literal
+ jQuery.extend( jQuery.Event(type), event ) :
+ // Just the event type (string)
+ jQuery.Event(type);
+
+ if ( type.indexOf("!") >= 0 ) {
+ event.type = type = type.slice(0, -1);
+ event.exclusive = true;
+ }
+
+ // Handle a global trigger
+ if ( !elem ) {
+ // Don't bubble custom events when global (to avoid too much overhead)
+ event.stopPropagation();
+ // Only trigger if we've ever bound an event for it
+ if ( this.global[type] )
+ jQuery.each( jQuery.cache, function(){
+ if ( this.events && this.events[type] )
+ jQuery.event.trigger( event, data, this.handle.elem );
+ });
+ }
- // Handle triggering a single element
- } else {
+ // Handle triggering a single element
// don't do events on text and comment nodes
- if ( elem.nodeType == 3 || elem.nodeType == 8 )
+ if ( !elem || elem.nodeType == 3 || elem.nodeType == 8 )
return undefined;
+ // Clean up in case it is reused
+ event.result = undefined;
+ event.target = elem;
+
// Clone the incoming data, if any
data = jQuery.makeArray(data);
-
- // AT_TARGET phase (not bubbling)
- if( !event.target ){
- // Clean up in case it is reused
- event.result = undefined;
- event.target = elem;
- }
-
- // Fix for custom events
- event.currentTarget = elem;
-
data.unshift( event );
+ }
- var fn = jQuery.isFunction( elem[ type ] );
-
- // Trigger the event, it is assumed that "handle" is a function
- var handle = jQuery.data(elem, "handle");
- if ( handle )
- handle.apply( elem, data );
-
- // Handle triggering native .onfoo handlers (and on links since we don't call .click() for links)
- if ( (!fn || (jQuery.nodeName(elem, 'a') && type == "click")) && elem["on"+type] && elem["on"+type].apply( elem, data ) === false )
- event.result = false;
-
- // Extra functions don't get the custom event object
- data.shift();
+ event.currentTarget = elem;
- // Handle triggering of extra function
- if ( extra && jQuery.isFunction( extra ) ) {
- // call the extra function and tack the current return value on the end for possible inspection
- var ret = extra.apply( elem, event.result == null ? data : data.concat( event.result ) );
- // if anything is returned, give it precedence and have it overwrite the previous value
- if ( ret !== undefined )
- event.result = ret;
- }
+ // Trigger the event, it is assumed that "handle" is a function
+ var handle = jQuery.data(elem, "handle");
+ if ( handle )
+ handle.apply( elem, data );
- // Trigger the native events (except for clicks on links)
- if ( event.target === elem && fn && !event.isDefaultPrevented() && !(jQuery.nodeName(elem, 'a') && type == "click") ) {
- this.triggered = true;
- try {
- elem[ type ]();
- // prevent IE from throwing an error for some hidden elements
- } catch (e) {}
- }
+ // Handle triggering native .onfoo handlers (and on links since we don't call .click() for links)
+ if ( (!elem[type] || (jQuery.nodeName(elem, 'a') && type == "click")) && elem["on"+type] && elem["on"+type].apply( elem, data ) === false )
+ event.result = false;
- if ( !event.isPropagationStopped() ) {
- var parent = elem.parentNode || elem.ownerDocument;
- if ( parent )
- jQuery.event.trigger(event, data, parent);
- }
+ // Trigger the native events (except for clicks on links)
+ if ( !bubbling && elem[type] && !event.isDefaultPrevented() && !(jQuery.nodeName(elem, 'a') && type == "click") ) {
+ this.triggered = true;
+ try {
+ elem[ type ]();
+ // prevent IE from throwing an error for some hidden elements
+ } catch (e) {}
+ }
- // Clean up, in case the event object is reused
- event.target = null;
+ this.triggered = false;
- this.triggered = false;
+ if ( !event.isPropagationStopped() ) {
+ var parent = elem.parentNode || elem.ownerDocument;
+ if ( parent )
+ jQuery.event.trigger(event, data, parent, true);
}
},
var all, handlers;
event = arguments[0] = jQuery.event.fix( event || window.event );
+ event.currentTarget = this;
// Namespaced event handlers
var namespaces = event.type.split(".");
// Cache this now, all = true means, any handler
all = !namespaces.length && !event.exclusive;
-
- var namespace = RegExp("(^|\\.)" + namespaces.slice().sort().join(".*\\.") + "(\\.|$)");
+
+ var namespace = new RegExp("(^|\\.)" + namespaces.slice().sort().join(".*\\.") + "(\\.|$)");
handlers = ( jQuery.data(this, "events") || {} )[event.type];
},
proxy: function( fn, proxy ){
+ proxy = proxy || function(){ return fn.apply(this, arguments); };
// Set the guid of unique handler to the same of original handler, so it can be removed
proxy.guid = fn.guid = fn.guid || proxy.guid || this.guid++;
// So proxy can be declared as an argument
teardown: function() {}
}
},
-
+
specialAll: {
live: {
setup: function( selector, namespaces ){
},
teardown: function( namespaces ){
if ( namespaces.length ) {
- var remove = 0, name = RegExp("(^|\\.)" + namespaces[0] + "(\\.|$)");
-
+ var remove = 0, name = new RegExp("(^|\\.)" + namespaces[0] + "(\\.|$)");
+
jQuery.each( (jQuery.data(this, "events").live || {}), function(){
if ( name.test(this.type) )
remove++;
});
-
- if ( remove <= 1 )
+
+ if ( remove < 1 )
jQuery.event.remove( this, namespaces[0], liveHandler );
}
}
// Allow instantiation without the 'new' keyword
if( !this.preventDefault )
return new jQuery.Event(src);
-
+
// Event object
if( src && src.type ){
this.originalEvent = src;
this.type = src.type;
-
- // Fix timeStamp
- this.timeStamp = src.timeStamp || now();
// Event type
}else
this.type = src;
+ // timeStamp is buggy for some events on Firefox(#3843)
+ // So we won't rely on the native value
+ this.timeStamp = now();
+
// Mark it as fixed
this[expando] = true;
};
while ( parent && parent != this )
try { parent = parent.parentNode; }
catch(e) { parent = this; }
-
+
if( parent != this ){
// set the correct event type
event.type = event.data;
jQuery.event.handle.apply( this, arguments );
}
};
-
-jQuery.each({
- mouseover: 'mouseenter',
+
+jQuery.each({
+ mouseover: 'mouseenter',
mouseout: 'mouseleave'
}, function( orig, fix ){
jQuery.event.special[ fix ] = {
teardown: function(){
jQuery.event.remove( this, orig, withinElement );
}
- };
+ };
});
jQuery.fn.extend({
});
},
- trigger: function( type, data, fn ) {
+ trigger: function( type, data ) {
return this.each(function(){
- jQuery.event.trigger( type, data, this, fn );
+ jQuery.event.trigger( type, data, this );
});
},
- triggerHandler: function( type, data, fn ) {
+ triggerHandler: function( type, data ) {
if( this[0] ){
var event = jQuery.Event(type);
event.preventDefault();
event.stopPropagation();
- jQuery.event.trigger( event, data, this[0], fn );
+ jQuery.event.trigger( event, data, this[0] );
return event.result;
- }
+ }
},
toggle: function( fn ) {
// Otherwise, remember the function for later
else
// Add the function to the wait list
- jQuery.readyList.push( function() { return fn.call(this, jQuery); } );
+ jQuery.readyList.push( fn );
return this;
},
-
+
live: function( type, fn ){
- jQuery(document).bind( liveConvert(type, this.selector), this.selector, fn );
+ var proxy = jQuery.event.proxy( fn );
+ proxy.guid += this.selector + type;
+
+ jQuery( this.context ).bind( liveConvert(type, this.selector), this.selector, proxy );
+
return this;
},
-
+
die: function( type, fn ){
- jQuery(document).unbind( liveConvert(type, this.selector), fn );
+ jQuery( this.context ).unbind( liveConvert(type, this.selector), fn ? { guid: fn.guid + this.selector + type } : null );
return this;
}
});
function liveHandler( event ){
- var check = RegExp("(^|\\.)" + event.type + "(\\.|$)"),
- stop = true;
+ var check = new RegExp("(^|\\.)" + event.type + "(\\.|$)"),
+ stop = true,
+ elems = [];
jQuery.each(jQuery.data(this, "events").live || [], function(i, fn){
if ( check.test(fn.type) ) {
var elem = jQuery(event.target).closest(fn.data)[0];
- if ( elem && fn.call(elem, event, fn.data) === false )
- stop = false;
+ if ( elem )
+ elems.push({ elem: elem, fn: fn });
}
});
+
+ elems.sort(function(a,b) {
+ return jQuery.data(a.elem, "closest") - jQuery.data(b.elem, "closest");
+ });
+
+ jQuery.each(elems, function(){
+ event.currentTarget = this.elem;
+ if ( this.fn.call(this.elem, event, this.fn.data) === false )
+ return (stop = false);
+ });
+
return stop;
}
function liveConvert(type, selector){
- return ["live", type, selector.replace(/\./g, "_")].join(".");
+ return ["live", type, selector.replace(/\./g, "`").replace(/ /g, "|")].join(".");
}
jQuery.extend({
if ( jQuery.readyList ) {
// Execute all of them
jQuery.each( jQuery.readyList, function(){
- this.call( document );
+ this.call( document, jQuery );
});
// Reset the list of functions
// If IE and not an iframe
// continually check to see if the document is ready
- if ( document.documentElement.doScroll && !window.frameElement ) (function(){
+ if ( document.documentElement.doScroll && window == window.top ) (function(){
if ( jQuery.isReady ) return;
try {
// Prevent memory leaks in IE
// And prevent errors on refresh with events like mouseover in other browsers
// Window isn't included so as not to unbind existing unload events
-jQuery( window ).bind( 'unload', function(){
+// More info:
+// - http://isaacschlueter.com/2006/10/msie-memory-leaks/
+// - https://bugzilla.mozilla.org/show_bug.cgi?id=252542
+jQuery( window ).bind( 'unload', function(){
for ( var id in jQuery.cache )
// Skip the window
if ( id != 1 && jQuery.cache[ id ].handle )
jQuery.event.remove( jQuery.cache[ id ].handle.elem );
-});
+});