propHooks now an object with props array and filter function.

Use the originalEvent to grab properties in filter functions since they often won't have been copied to event. Mark a few current props in the main jQuery.event.props list as deprecated, they aren't supported across all browsers.
This commit is contained in:
Dave Methvin 2011-09-25 19:56:34 -04:00
parent ae27424b30
commit b4120a7430
2 changed files with 95 additions and 145 deletions

View File

@ -7,6 +7,8 @@ var rnamespaces = /\.(.*)$/,
rescape = /[^\w\s.|`]/g,
rtypenamespace = /^([^\.]*)?(?:\.(.+))?$/,
rhoverHack = /\bhover(\.\S+)?/,
rkeyEvent = /^key/,
rmouseEvent = /^(?:mouse|contextmenu)|click/,
rquickIs = /^([\w\-]+)?(?:#([\w\-]+))?(?:\.([\w\-]+))?(?:\[([\w+\-]+)=["']?([\w\-]*)["']?\])?$/,
quickParse = function( selector ) {
var quick = rquickIs.exec( selector );
@ -407,12 +409,6 @@ jQuery.event = {
// Make a writable jQuery.Event from the native event object
event = jQuery.event.fix( event || window.event );
var propHook = jQuery.event.propHooks[ event.type ];
if ( propHook ) {
event = propHook( event );
}
var handlers = ((jQuery._data( this, "events" ) || {})[ event.type ] || []),
delegateCount = handlers.delegateCount,
args = Array.prototype.slice.call( arguments, 0 ),
@ -469,55 +465,89 @@ jQuery.event = {
return event.result;
},
props: "altKey attrName bubbles button cancelable charCode ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view which".split(" "),
// Includes some event props shared by KeyEvent and MouseEvent
// *** attrChange attrName relatedNode srcElement are not normalized, non-W3C, deprecated, will be removed in 1.8 ***
props: "attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp type view which".split(" "),
propHooks: {},
keyHooks: {
props: "char charCode key keyCode".split(" "),
filter: function( event, original ) {
// Add which for key events
if ( event.which == null ) {
event.which = original.charCode != null ? original.charCode : original.keyCode;
}
return event;
}
},
mouseHooks: {
props: "button buttons clientX clientY fromElement layerX layerY offsetX offsetY pageX pageY screenX screenY toElement wheelDelta".split(" "),
filter: function( event, original ) {
var eventDoc, doc, body,
button = original.button;
// Calculate pageX/Y if missing and clientX/Y available
if ( event.pageX == null && original.clientX != null ) {
eventDoc = event.target.ownerDocument || document;
doc = eventDoc.documentElement;
body = eventDoc.body;
event.pageX = original.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
event.pageY = original.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
}
// Add relatedTarget, if necessary
if ( !event.relatedTarget && original.fromElement ) {
event.relatedTarget = original.fromElement === event.target ? original.toElement : original.fromElement;
}
// Add which for click: 1 === left; 2 === middle; 3 === right
// Note: button is not normalized, so don't use it
if ( !event.which && button !== undefined ) {
event.which = (button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ));
}
return event;
}
},
fix: function( event ) {
if ( event[ jQuery.expando ] ) {
return event;
}
// store a copy of the original event object
// and "clone" to set read-only properties
// Create a writable copy of the event object and normalize some properties
var originalEvent = event,
propHook = jQuery.event.propHooks[ event.type ],
copy = this.props;
propHook = jQuery.event.propHooks[ event.type ] || {},
copy = propHook.props? this.props.concat( propHook.props ) : this.props;
event = jQuery.Event( originalEvent );
if ( propHook ) {
copy.push.apply( copy, propHook() || [] );
}
for ( var i = copy.length, prop; i; ) {
prop = copy[ --i ];
event[ prop ] = originalEvent[ prop ];
}
// Fix target property, if necessary
// Removal will crash IE6,7,8
// Fix target property, if necessary (#1925, IE 6/7/8 & Safari2)
if ( !event.target ) {
// Fixes #1925 where srcElement might not be defined either
event.target = event.srcElement || document;
event.target = originalEvent.srcElement || document;
}
// check if target is a textnode (safari)
// Removal will crash IE6,7,8
// Target should not be a text node (#504, Safari)
if ( event.target.nodeType === 3 ) {
event.target = event.target.parentNode;
}
// Add relatedTarget, if necessary
if ( !event.relatedTarget && event.fromElement ) {
event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
// For mouse/key events; add metaKey if it's not there (#3368, IE6/7/8)
if ( event.metaKey === undefined ) {
event.metaKey = event.ctrlKey;
}
if ( propHook ) {
event = propHook( event, originalEvent );
}
return event;
return propHook.filter? propHook.filter( event, originalEvent ) : event;
},
// Deprecated, use jQuery.guid instead
@ -1026,58 +1056,14 @@ jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblcl
jQuery.attrFn[ name ] = true;
}
// Key Event property hooks
if ( rkeyEvent.test( name ) ) {
jQuery.event.propHooks[ name ] = function( event, original ) {
var charCode = event.charCode,
keyCode = event.keyCode,
ctrlKey = event.ctrlKey;
// Add which for key events
if ( event.which == null && (charCode != null || keyCode != null) ) {
event.which = charCode != null ? charCode : keyCode;
}
// Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
if ( !event.metaKey && ctrlKey ) {
event.metaKey = ctrlKey;
}
return event;
};
jQuery.event.propHooks[ name ] = jQuery.event.keyHooks;
}
// Mouse Event property hooks
if ( rmouseEvent.test( name ) ) {
jQuery.event.propHooks[ name ] = function( event, original ) {
if ( !event ) {
return mouseProps;
}
var eventDoc, doc, body,
button = event.button;
// Calculate pageX/Y if missing and clientX/Y available
if ( event.pageX == null && event.clientX != null ) {
eventDoc = event.target.ownerDocument || document;
doc = eventDoc.documentElement;
body = eventDoc.body;
event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
}
// Add which for click: 1 === left; 2 === middle; 3 === right
// Note: button is not normalized, so don't use it
if ( !event.which && button !== undefined ) {
event.which = (button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ));
}
return event;
};
}});
jQuery.event.propHooks[ name ] = jQuery.event.mouseHooks;
}
});
})( jQuery );

View File

@ -2383,6 +2383,38 @@ test("delegated events quickIs", function() {
markup.remove();
});
test("propHooks extensions", function() {
expect( 3 );
jQuery( "<a id='hook-fixture' href=''></a>" ).appendTo( "#qunit-fixture" );
var $fixture = jQuery( "#hook-fixture" );
// Ensure the property doesn't exist
$fixture.bind( "focus", function( event ) {
ok( !("blurrinessLevel" in event), "event.blurrinessLevel does not exist" );
})[0].focus();
// Must blur the link so focus works below
$fixture.unbind( "focus" )[0].blur();
// Define a custom property for "focus" events via the filter function
ok( !jQuery.event.propHooks.focus, "We aren't clobbering an existing focus hook" );
jQuery.event.propHooks.focus = {
filter: function( event, originalEvent ) {
event.blurrinessLevel = 42;
return event;
}
};
// Trigger a native focus and ensure the property is set
$fixture.bind( "focus", function( event ) {
equals( event.blurrinessLevel, 42, "event.blurrinessLevel was set" );
})[0].focus();
delete jQuery.event.propHooks.focus;
$fixture.unbind( "focus" ).remove();
});
(function(){
// This code must be run before DOM ready!
@ -2457,72 +2489,4 @@ test("delegated events quickIs", function() {
})();
test("jQuery.event.propHooks", function() {
expect( 1 );
ok( jQuery.event.propHooks, "jQuery.event.propHooks exists" );
});
test("jQuery.event.propHooks as function", function() {
expect( 2 );
jQuery( "<div id='hook-fixture'></div>" ).appendTo( "#qunit-fixture" );
var $fixture = jQuery( "#hook-fixture" );
// Does not exist
$fixture.bind( "click", function( event ) {
ok( !("propC" in event), "event.propC Does not exist" );
}).trigger( "click" );
$fixture.unbind( "click" );
// Store as function
jQuery.event.propHooks[ "custom" ] = function( event ) {
// receives the event object for processing
event.propC = true;
return event;
};
$fixture.bind( "custom", function( event ) {
ok( event.propC, "event.propC exists" );
}).trigger( "custom" );
});
test("jQuery.event.propHooks usecase", function() {
expect( 3 );
jQuery( "<div id='hook-fixture'></div>" ).appendTo( "#qunit-fixture" );
var $fixture = jQuery( "#hook-fixture" );
$fixture.bind( "fakedrop", function( event ) {
ok( !("dataTransfer" in event), "event.dataTransfer is not available" );
}).trigger( "fakedrop" );
$fixture.unbind( "fakedrop" );
jQuery.event.propHooks[ "fakedrop" ] = function( event ) {
event.dataTransfer = "some val";
return event;
};
$fixture.bind( "fakedrop", function( event ) {
ok( ("dataTransfer" in event), "event.dataTransfer exists, just copied" );
equal( event.dataTransfer, "some val", "event.dataTransfer equal 'some val'" );
}).trigger( "fakedrop" );
$fixture.unbind( "fakedrop" );
});
/*
test("event properties", function() {
stop();
jQuery("#simon1").click(function(event) {
ok( event.timeStamp, "assert event.timeStamp is present" );
start();
}).click();
});
*/