Skip to content

Commit

Permalink
Event: Separate trigger/simulate into its own module
Browse files Browse the repository at this point in the history
Fixes jquerygh-1864

This also pulls the focusin/out special event into its own module, since that
depends on simulate(). NB: The ajax module triggers events pretty heavily.
  • Loading branch information
dmethvin committed Nov 5, 2015
1 parent 769446c commit 4f3ee78
Show file tree
Hide file tree
Showing 6 changed files with 259 additions and 226 deletions.
2 changes: 2 additions & 0 deletions src/ajax.js
Expand Up @@ -5,9 +5,11 @@ define( [
"./ajax/var/location",
"./ajax/var/nonce",
"./ajax/var/rquery",

"./core/init",
"./ajax/parseJSON",
"./ajax/parseXML",
"./event/trigger",
"./deferred"
], function( jQuery, document, rnotwhite, location, nonce, rquery ) {

Expand Down
226 changes: 1 addition & 225 deletions src/event.js
Expand Up @@ -2,20 +2,16 @@ define( [
"./core",
"./var/document",
"./var/rnotwhite",
"./var/hasOwn",
"./var/slice",
"./event/support",
"./data/var/dataPriv",
"./data/var/acceptData",

"./core/init",
"./selector"
], function( jQuery, document, rnotwhite, hasOwn, slice, support, dataPriv, acceptData ) {
], function( jQuery, document, rnotwhite, slice, dataPriv ) {

var
rkeyEvent = /^key/,
rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/,
rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
rtypenamespace = /^([^.]*)(?:\.(.+)|)/;

function returnTrue() {
Expand Down Expand Up @@ -283,141 +279,6 @@ jQuery.event = {
}
},

trigger: function( event, data, elem, onlyHandlers ) {

var i, cur, tmp, bubbleType, ontype, handle, special,
eventPath = [ elem || document ],
type = hasOwn.call( event, "type" ) ? event.type : event,
namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : [];

cur = tmp = elem = elem || document;

// Don't do events on text and comment nodes
if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
return;
}

// focus/blur morphs to focusin/out; ensure we're not firing them right now
if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
return;
}

if ( type.indexOf( "." ) > -1 ) {

// Namespaced trigger; create a regexp to match event type in handle()
namespaces = type.split( "." );
type = namespaces.shift();
namespaces.sort();
}
ontype = type.indexOf( ":" ) < 0 && "on" + type;

// Caller can pass in a jQuery.Event object, Object, or just an event type string
event = event[ jQuery.expando ] ?
event :
new jQuery.Event( type, typeof event === "object" && event );

// Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)
event.isTrigger = onlyHandlers ? 2 : 3;
event.namespace = namespaces.join( "." );
event.rnamespace = event.namespace ?
new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) :
null;

// Clean up the event in case it is being reused
event.result = undefined;
if ( !event.target ) {
event.target = elem;
}

// Clone any incoming data and prepend the event, creating the handler arg list
data = data == null ?
[ event ] :
jQuery.makeArray( data, [ event ] );

// Allow special events to draw outside the lines
special = jQuery.event.special[ type ] || {};
if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
return;
}

// Determine event propagation path in advance, per W3C events spec (#9951)
// Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {

bubbleType = special.delegateType || type;
if ( !rfocusMorph.test( bubbleType + type ) ) {
cur = cur.parentNode;
}
for ( ; cur; cur = cur.parentNode ) {
eventPath.push( cur );
tmp = cur;
}

// Only add window if we got to document (e.g., not plain obj or detached DOM)
if ( tmp === ( elem.ownerDocument || document ) ) {
eventPath.push( tmp.defaultView || tmp.parentWindow || window );
}
}

// Fire handlers on the event path
i = 0;
while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) {

event.type = i > 1 ?
bubbleType :
special.bindType || type;

// jQuery handler
handle = ( dataPriv.get( cur, "events" ) || {} )[ event.type ] &&
dataPriv.get( cur, "handle" );
if ( handle ) {
handle.apply( cur, data );
}

// Native handler
handle = ontype && cur[ ontype ];
if ( handle && handle.apply && acceptData( cur ) ) {
event.result = handle.apply( cur, data );
if ( event.result === false ) {
event.preventDefault();
}
}
}
event.type = type;

// If nobody prevented the default action, do it now
if ( !onlyHandlers && !event.isDefaultPrevented() ) {

if ( ( !special._default ||
special._default.apply( eventPath.pop(), data ) === false ) &&
acceptData( elem ) ) {

// Call a native DOM method on the target with the same name name as the event.
// Don't do default actions on window, that's where global variables be (#6170)
if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) {

// Don't re-trigger an onFOO event when we call its FOO() method
tmp = elem[ ontype ];

if ( tmp ) {
elem[ ontype ] = null;
}

// Prevent re-triggering of the same event, since we already bubbled it above
jQuery.event.triggered = type;
elem[ type ]();
jQuery.event.triggered = undefined;

if ( tmp ) {
elem[ ontype ] = tmp;
}
}
}
}

return event.result;
},

dispatch: function( event ) {

// Make a writable jQuery.Event from the native event object
Expand Down Expand Up @@ -667,37 +528,6 @@ jQuery.event = {
}
}
}
},

// Piggyback on a donor event to simulate a different one
simulate: function( type, elem, event ) {
var e = jQuery.extend(
new jQuery.Event(),
event,
{
type: type,
isSimulated: true

// Previously, `originalEvent: {}` was set here, so stopPropagation call
// would not be triggered on donor event, since in our own
// jQuery.event.stopPropagation function we had a check for existence of
// originalEvent.stopPropagation method, so, consequently it would be a noop.
//
// But now, this "simulate" function is used only for events
// for which stopPropagation() is noop, so there is no need for that anymore.
//
// For the compat branch though, guard for "click" and "submit"
// events is still used, but was moved to jQuery.event.stopPropagation function
// because `originalEvent` should point to the original event for the constancy
// with other events and for more focused logic
}
);

jQuery.event.trigger( e, null, elem );

if ( e.isDefaultPrevented() ) {
event.preventDefault();
}
}
};

Expand Down Expand Up @@ -823,48 +653,6 @@ jQuery.each( {
};
} );

// Support: Firefox
// Firefox doesn't have focus(in | out) events
// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787
//
// Support: Chrome, Safari
// focus(in | out) events fire after focus & blur events,
// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order
// Related ticket - https://code.google.com/p/chromium/issues/detail?id=449857
if ( !support.focusin ) {
jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) {

// Attach a single capturing handler on the document while someone wants focusin/focusout
var handler = function( event ) {
jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) );
};

jQuery.event.special[ fix ] = {
setup: function() {
var doc = this.ownerDocument || this,
attaches = dataPriv.access( doc, fix );

if ( !attaches ) {
doc.addEventListener( orig, handler, true );
}
dataPriv.access( doc, fix, ( attaches || 0 ) + 1 );
},
teardown: function() {
var doc = this.ownerDocument || this,
attaches = dataPriv.access( doc, fix ) - 1;

if ( !attaches ) {
doc.removeEventListener( orig, handler, true );
dataPriv.remove( doc, fix );

} else {
dataPriv.access( doc, fix, attaches );
}
}
};
} );
}

jQuery.fn.extend( {

on: function( types, selector, data, fn ) {
Expand Down Expand Up @@ -908,18 +696,6 @@ jQuery.fn.extend( {
return this.each( function() {
jQuery.event.remove( this, types, fn, selector );
} );
},

trigger: function( type, data ) {
return this.each( function() {
jQuery.event.trigger( type, data, this );
} );
},
triggerHandler: function( type, data ) {
var elem = this[ 0 ];
if ( elem ) {
return jQuery.event.trigger( type, data, elem, true );
}
}
} );

Expand Down
4 changes: 3 additions & 1 deletion src/event/alias.js
@@ -1,6 +1,8 @@
define( [
"../core",
"../event"

"../event",
"./trigger"
], function( jQuery ) {

jQuery.each( ( "blur focus focusin focusout resize scroll click dblclick " +
Expand Down
53 changes: 53 additions & 0 deletions src/event/focusin.js
@@ -0,0 +1,53 @@
define( [
"../core",
"../data/var/dataPriv",
"./support",

"../event",
"./trigger"
], function( jQuery, dataPriv, support ) {

// Support: Firefox
// Firefox doesn't have focus(in | out) events
// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787
//
// Support: Chrome, Safari
// focus(in | out) events fire after focus & blur events,
// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order
// Related ticket - https://code.google.com/p/chromium/issues/detail?id=449857
if ( !support.focusin ) {
jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) {

// Attach a single capturing handler on the document while someone wants focusin/focusout
var handler = function( event ) {
jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) );
};

jQuery.event.special[ fix ] = {
setup: function() {
var doc = this.ownerDocument || this,
attaches = dataPriv.access( doc, fix );

if ( !attaches ) {
doc.addEventListener( orig, handler, true );
}
dataPriv.access( doc, fix, ( attaches || 0 ) + 1 );
},
teardown: function() {
var doc = this.ownerDocument || this,
attaches = dataPriv.access( doc, fix ) - 1;

if ( !attaches ) {
doc.removeEventListener( orig, handler, true );
dataPriv.remove( doc, fix );

} else {
dataPriv.access( doc, fix, attaches );
}
}
};
} );
}

return jQuery;
} );

0 comments on commit 4f3ee78

Please sign in to comment.