-var fcleanup = function( nm ) {
- return nm.replace(/[^\w\s\.\|`]/g, function( ch ) {
- return "\\" + ch;
- });
-};
+var rnamespaces = /\.(.*)$/,
+ fcleanup = function( nm ) {
+ return nm.replace(/[^\w\s\.\|`]/g, function( ch ) {
+ return "\\" + ch;
+ });
+ };
/*
* A number of helper functions used for managing events.
elem = window;
}
+ var handleObjIn, handleObj;
+
+ if ( handler.handler ) {
+ handleObjIn = handler;
+ handler = handleObjIn.handler;
+ }
+
// Make sure that the function being executed has a unique ID
if ( !handler.guid ) {
handler.guid = jQuery.guid++;
var type, i = 0, namespaces;
while ( (type = types[ i++ ]) ) {
- var handleObj = {
- handler: handler,
- data: data,
- namespace: "",
- guid: handler.guid
- };
+ handleObj = handleObjIn ?
+ jQuery.extend({}, handleObjIn) :
+ { handler: handler, data: data };
// Namespaced event handlers
if ( type.indexOf(".") > -1 ) {
} else {
namespaces = [];
+ handleObj.namespace = "";
}
handleObj.type = type;
+ handleObj.guid = handler.guid;
// Get the current list of functions bound to this event
var handlers = events[ type ],
// Check for a special event handler
// Only use addEventListener/attachEvent if the special
// events handler returns false
- if ( !special.setup || special.setup.call( elem, data, namespaces, handler ) === false ) {
+ if ( !special.setup || special.setup.call( elem, data, namespaces ) === false ) {
// Bind the global event handler to the element
if ( elem.addEventListener ) {
elem.addEventListener( type, eventHandle, false );
if ( special.add ) {
special.add.call( elem, handleObj );
}
-
+
// Add the function to the element's handler list
handlers.push( handleObj );
if ( handler.guid === handleObj.guid ) {
// remove the given handler for the given type
if ( all || namespace.test( handleObj.namespace ) ) {
- fn = handleObj.handler;
-
if ( pos == null ) {
eventType.splice( j--, 1 );
}
if ( special.remove ) {
- special.remove.call( elem, namespaces, handleObj );
+ special.remove.call( elem, handleObj );
}
}
// So that we can later remove it
event.handler = handleObj.handler;
event.data = handleObj.data;
+ event.handleObj = handleObj;
var ret = handleObj.handler.apply( this, arguments );
},
live: {
- add: function( proxy, data, namespaces, live ) {
- jQuery.extend( proxy, data || {} );
-
- proxy.guid += data.selector + data.live;
- data.liveProxy = proxy;
-
- jQuery.event.add( this, data.live, liveHandler, data );
-
+ add: function( handleObj ) {
+ jQuery.event.add( this, handleObj.origType, jQuery.extend({}, handleObj, {handler: liveHandler}) );
},
- remove: function( namespaces ) {
- if ( namespaces.length ) {
- 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 ) {
- jQuery.event.remove( this, namespaces[0], liveHandler );
+ remove: function( handleObj ) {
+ var remove = true,
+ type = handleObj.origType.replace(rnamespaces, "");
+
+ jQuery.each( jQuery.data(this, "events").live || [], function() {
+ if ( type === this.origType.replace(rnamespaces, "") ) {
+ remove = false;
+ return false;
}
+ });
+
+ if ( remove ) {
+ jQuery.event.remove( this, handleObj.origType, liveHandler );
}
- },
- special: {}
+ }
+
},
+
beforeunload: {
setup: function( data, namespaces, fn ) {
// We only want to do this special case on windows
jQuery.each(["live", "die"], function( i, name ) {
jQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) {
- var type, i = 0,
+ var type, i = 0, match, namespaces,
selector = origSelector || this.selector,
context = origSelector ? this : jQuery( this.context );
data = undefined;
}
- types = (types || "").split( /\s+/ );
+ types = (types || "").split(" ");
while ( (type = types[ i++ ]) != null ) {
+ match = rnamespaces.exec( type );
+ namespaces = "";
+
+ if ( match ) {
+ namespaces = match[0];
+ type = type.replace( rnamespaces, "" );
+ }
+
type = type === "focus" ? "focusin" : // focus --> focusin
type === "blur" ? "focusout" : // blur --> focusout
- type === "hover" ? types.push("mouseleave") && "mouseenter" : // hover support
+ type === "hover" ? types.push("mouseleave" + namespaces) && "mouseenter" : // hover support
type;
-
+
+ type += namespaces;
+
if ( name === "live" ) {
// bind live handler
- context.bind( liveConvert( type, selector ), {
- data: data, selector: selector, live: type
- }, fn );
+ context.each(function(){
+ jQuery.event.add( this, liveConvert( type, selector ),
+ { data: data, selector: selector, handler: fn, origType: type, origHandler: fn } );
+ });
} else {
// unbind live handler
- context.unbind( liveConvert( type, selector ), fn ? { guid: fn.guid + selector + type } : null );
+ context.unbind( liveConvert( type, selector ), fn );
}
}
function liveHandler( event ) {
var stop, elems = [], selectors = [], args = arguments,
- related, match, fn, elem, j, i, l, data,
- live = jQuery.extend({}, jQuery.data( this, "events" ).live);
+ related, match, handleObj, elem, j, i, l, data,
+ events = jQuery.data( this, "events" );
// Make sure we avoid non-left-click bubbling in Firefox (#3861)
- if ( event.button && event.type === "click" ) {
+ if ( event.liveFired === this || !events || event.button && event.type === "click" ) {
return;
}
- for ( j in live ) {
- fn = live[j];
- if ( fn.live === event.type ||
- fn.altLive && jQuery.inArray(event.type, fn.altLive) > -1 ) {
+ event.liveFired = this;
+
+ var live = events.live.slice(0);
+
+ for ( j = 0; j < live.length; j++ ) {
+ handleObj = live[j];
+
+ if ( handleObj.origType.replace( rnamespaces, "" ) === event.type ) {
+ selectors.push( handleObj.selector );
- data = fn.data;
- if ( !(data.beforeFilter && data.beforeFilter[event.type] &&
- !data.beforeFilter[event.type](event)) ) {
- selectors.push( fn.selector );
- }
} else {
- delete live[j];
+ live.splice( j--, 1 );
}
}
match = jQuery( event.target ).closest( selectors, event.currentTarget );
for ( i = 0, l = match.length; i < l; i++ ) {
- for ( j in live ) {
- fn = live[j];
- elem = match[i].elem;
- related = null;
+ for ( j = 0; j < live.length; j++ ) {
+ handleObj = live[j];
+
+ if ( match[i].selector === handleObj.selector ) {
+ elem = match[i].elem;
+ related = null;
- if ( match[i].selector === fn.selector ) {
// Those two events require additional checking
- if ( fn.live === "mouseenter" || fn.live === "mouseleave" ) {
- related = jQuery( event.relatedTarget ).closest( fn.selector )[0];
+ if ( handleObj.origType === "mouseenter" || handleObj.origType === "mouseleave" ) {
+ related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0];
}
if ( !related || related !== elem ) {
- elems.push({ elem: elem, fn: fn });
+ elems.push({ elem: elem, handleObj: handleObj });
}
}
}
for ( i = 0, l = elems.length; i < l; i++ ) {
match = elems[i];
event.currentTarget = match.elem;
- event.data = match.fn.data;
- if ( match.fn.apply( match.elem, args ) === false ) {
+ event.data = match.handleObj.data;
+ event.handleObj = match.handleObj;
+
+ if ( match.handleObj.origHandler.apply( match.elem, args ) === false ) {
stop = false;
break;
}
equals( liveb, 0, "Click on body" );
// This should trigger two events
+ submit = 0, div = 0, livea = 0, liveb = 0;
jQuery("div#nothiddendiv").trigger("click");
equals( submit, 0, "Click on div" );
equals( div, 1, "Click on div" );
equals( liveb, 0, "Click on div" );
// This should trigger three events (w/ bubbling)
+ submit = 0, div = 0, livea = 0, liveb = 0;
jQuery("div#nothiddendivchild").trigger("click");
equals( submit, 0, "Click on inner div" );
- equals( div, 2, "Click on inner div" );
- equals( livea, 2, "Click on inner div" );
+ equals( div, 1, "Click on inner div" );
+ equals( livea, 1, "Click on inner div" );
equals( liveb, 1, "Click on inner div" );
// This should trigger one submit
+ submit = 0, div = 0, livea = 0, liveb = 0;
jQuery("div#nothiddendivchild").trigger("submit");
equals( submit, 1, "Submit on div" );
- equals( div, 2, "Submit on div" );
- equals( livea, 2, "Submit on div" );
- equals( liveb, 1, "Submit on div" );
+ equals( div, 0, "Submit on div" );
+ equals( livea, 0, "Submit on div" );
+ equals( liveb, 0, "Submit on div" );
// Make sure no other events were removed in the process
+ submit = 0, div = 0, livea = 0, liveb = 0;
jQuery("div#nothiddendivchild").trigger("click");
- equals( submit, 1, "die Click on inner div" );
- equals( div, 3, "die Click on inner div" );
- equals( livea, 3, "die Click on inner div" );
- equals( liveb, 2, "die Click on inner div" );
+ equals( submit, 0, "die Click on inner div" );
+ equals( div, 1, "die Click on inner div" );
+ equals( livea, 1, "die Click on inner div" );
+ equals( liveb, 1, "die Click on inner div" );
// Now make sure that the removal works
+ submit = 0, div = 0, livea = 0, liveb = 0;
jQuery("div#nothiddendivchild").die("click");
jQuery("div#nothiddendivchild").trigger("click");
- equals( submit, 1, "die Click on inner div" );
- equals( div, 4, "die Click on inner div" );
- equals( livea, 4, "die Click on inner div" );
- equals( liveb, 2, "die Click on inner div" );
+ equals( submit, 0, "die Click on inner div" );
+ equals( div, 1, "die Click on inner div" );
+ equals( livea, 1, "die Click on inner div" );
+ equals( liveb, 0, "die Click on inner div" );
// Make sure that the click wasn't removed too early
+ submit = 0, div = 0, livea = 0, liveb = 0;
jQuery("div#nothiddendiv").trigger("click");
- equals( submit, 1, "die Click on inner div" );
- equals( div, 5, "die Click on inner div" );
- equals( livea, 5, "die Click on inner div" );
- equals( liveb, 2, "die Click on inner div" );
+ equals( submit, 0, "die Click on inner div" );
+ equals( div, 1, "die Click on inner div" );
+ equals( livea, 1, "die Click on inner div" );
+ equals( liveb, 0, "die Click on inner div" );
// Make sure that stopPropgation doesn't stop live events
+ submit = 0, div = 0, livea = 0, liveb = 0;
jQuery("div#nothiddendivchild").live("click", function(e){ liveb++; e.stopPropagation(); });
jQuery("div#nothiddendivchild").trigger("click");
- equals( submit, 1, "stopPropagation Click on inner div" );
- equals( div, 6, "stopPropagation Click on inner div" );
- equals( livea, 6, "stopPropagation Click on inner div" );
- equals( liveb, 3, "stopPropagation Click on inner div" );
+ equals( submit, 0, "stopPropagation Click on inner div" );
+ equals( div, 1, "stopPropagation Click on inner div" );
+ equals( livea, 1, "stopPropagation Click on inner div" );
+ equals( liveb, 1, "stopPropagation Click on inner div" );
// Make sure click events only fire with primary click
+ submit = 0, div = 0, livea = 0, liveb = 0;
var event = jQuery.Event("click");
event.button = 1;
jQuery("div#nothiddendiv").trigger(event);
- equals( livea, 6, "live secondary click" );
+ equals( livea, 0, "live secondary click" );
jQuery("div#nothiddendivchild").die("click");
jQuery("div#nothiddendiv").die("click");
equals( count, 2, "Make sure both the click and submit were triggered." );
});
+test("live with namespaces", function(){
+ expect(6);
+
+ var count1 = 0, count2 = 0;
+
+ jQuery("#liveSpan1").live("foo.bar", function(){
+ count1++;
+ });
+
+ jQuery("#liveSpan2").live("foo.zed", function(){
+ count2++;
+ });
+
+ jQuery("#liveSpan1").trigger("foo.bar");
+ equals( count1, 1, "Got live foo.bar" );
+
+ jQuery("#liveSpan2").trigger("foo.zed");
+ equals( count2, 1, "Got live foo.zed" );
+
+ //remove one
+ jQuery("#liveSpan2").die("foo.zed");
+ jQuery("#liveSpan1").trigger("foo.bar");
+
+ equals( count1, 2, "Got live foo.bar after dieing foo.zed" );
+
+ jQuery("#liveSpan2").trigger("foo.zed");
+ equals( count2, 1, "Got live foo.zed" );
+
+ //remove the other
+ jQuery("#liveSpan1").die("foo.bar");
+
+ jQuery("#liveSpan1").trigger("foo.bar");
+ equals( count1, 2, "Did not respond to foo.bar after dieing it" );
+
+ jQuery("#liveSpan2").trigger("foo.zed");
+ equals( count2, 1, "Did not trigger foo.zed again" );
+});
+
test("live with change", function(){
var selectChange = 0, checkboxChange = 0;