2017-05-03 15:35:00 +02:00
/ * *
* Copyright 2013 - present , Facebook , Inc .
* All rights reserved .
*
* This source code is licensed under the BSD - style license found in the
* LICENSE file in the root directory of this source tree . An additional grant
* of patent rights can be found in the PATENTS file in the same directory .
*
* /
'use strict' ;
var _assign = require ( 'object-assign' ) ;
var PooledClass = require ( './PooledClass' ) ;
var emptyFunction = require ( 'fbjs/lib/emptyFunction' ) ;
var warning = require ( 'fbjs/lib/warning' ) ;
var didWarnForAddedNewProperty = false ;
var isProxySupported = typeof Proxy === 'function' ;
var shouldBeReleasedProperties = [ 'dispatchConfig' , '_targetInst' , 'nativeEvent' , 'isDefaultPrevented' , 'isPropagationStopped' , '_dispatchListeners' , '_dispatchInstances' ] ;
/ * *
* @ interface Event
* @ see http : //www.w3.org/TR/DOM-Level-3-Events/
* /
var EventInterface = {
type : null ,
target : null ,
// currentTarget is set when dispatching; no use in copying it here
currentTarget : emptyFunction . thatReturnsNull ,
eventPhase : null ,
bubbles : null ,
cancelable : null ,
timeStamp : function ( event ) {
return event . timeStamp || Date . now ( ) ;
} ,
defaultPrevented : null ,
isTrusted : null
} ;
/ * *
* Synthetic events are dispatched by event plugins , typically in response to a
* top - level event delegation handler .
*
* These systems should generally use pooling to reduce the frequency of garbage
* collection . The system should check ` isPersistent ` to determine whether the
* event should be released into the pool after being dispatched . Users that
* need a persisted event should invoke ` persist ` .
*
* Synthetic events ( and subclasses ) implement the DOM Level 3 Events API by
* normalizing browser quirks . Subclasses do not necessarily have to implement a
* DOM interface ; custom application - specific events can also subclass this .
*
* @ param { object } dispatchConfig Configuration used to dispatch this event .
* @ param { * } targetInst Marker identifying the event target .
* @ param { object } nativeEvent Native browser event .
* @ param { DOMEventTarget } nativeEventTarget Target node .
* /
function SyntheticEvent ( dispatchConfig , targetInst , nativeEvent , nativeEventTarget ) {
if ( process . env . NODE _ENV !== 'production' ) {
// these have a getter/setter for warnings
delete this . nativeEvent ;
delete this . preventDefault ;
delete this . stopPropagation ;
}
this . dispatchConfig = dispatchConfig ;
this . _targetInst = targetInst ;
this . nativeEvent = nativeEvent ;
var Interface = this . constructor . Interface ;
for ( var propName in Interface ) {
if ( ! Interface . hasOwnProperty ( propName ) ) {
continue ;
}
if ( process . env . NODE _ENV !== 'production' ) {
delete this [ propName ] ; // this has a getter/setter for warnings
}
var normalize = Interface [ propName ] ;
if ( normalize ) {
this [ propName ] = normalize ( nativeEvent ) ;
} else {
if ( propName === 'target' ) {
this . target = nativeEventTarget ;
} else {
this [ propName ] = nativeEvent [ propName ] ;
}
}
}
var defaultPrevented = nativeEvent . defaultPrevented != null ? nativeEvent . defaultPrevented : nativeEvent . returnValue === false ;
if ( defaultPrevented ) {
this . isDefaultPrevented = emptyFunction . thatReturnsTrue ;
} else {
this . isDefaultPrevented = emptyFunction . thatReturnsFalse ;
}
this . isPropagationStopped = emptyFunction . thatReturnsFalse ;
return this ;
}
_assign ( SyntheticEvent . prototype , {
preventDefault : function ( ) {
this . defaultPrevented = true ;
var event = this . nativeEvent ;
if ( ! event ) {
return ;
}
if ( event . preventDefault ) {
event . preventDefault ( ) ;
2017-08-14 05:01:11 +02:00
// eslint-disable-next-line valid-typeof
2017-05-03 15:35:00 +02:00
} else if ( typeof event . returnValue !== 'unknown' ) {
event . returnValue = false ;
}
this . isDefaultPrevented = emptyFunction . thatReturnsTrue ;
} ,
stopPropagation : function ( ) {
var event = this . nativeEvent ;
if ( ! event ) {
return ;
}
if ( event . stopPropagation ) {
event . stopPropagation ( ) ;
2017-08-14 05:01:11 +02:00
// eslint-disable-next-line valid-typeof
2017-05-03 15:35:00 +02:00
} else if ( typeof event . cancelBubble !== 'unknown' ) {
// The ChangeEventPlugin registers a "propertychange" event for
// IE. This event does not support bubbling or cancelling, and
// any references to cancelBubble throw "Member not found". A
// typeof check of "unknown" circumvents this issue (and is also
// IE specific).
event . cancelBubble = true ;
}
this . isPropagationStopped = emptyFunction . thatReturnsTrue ;
} ,
/ * *
* We release all dispatched ` SyntheticEvent ` s after each event loop , adding
* them back into the pool . This allows a way to hold onto a reference that
* won ' t be added back into the pool .
* /
persist : function ( ) {
this . isPersistent = emptyFunction . thatReturnsTrue ;
} ,
/ * *
* Checks if this event should be released back into the pool .
*
* @ return { boolean } True if this should not be released , false otherwise .
* /
isPersistent : emptyFunction . thatReturnsFalse ,
/ * *
* ` PooledClass ` looks for ` destructor ` on each instance it releases .
* /
destructor : function ( ) {
var Interface = this . constructor . Interface ;
for ( var propName in Interface ) {
if ( process . env . NODE _ENV !== 'production' ) {
Object . defineProperty ( this , propName , getPooledWarningPropertyDefinition ( propName , Interface [ propName ] ) ) ;
} else {
this [ propName ] = null ;
}
}
for ( var i = 0 ; i < shouldBeReleasedProperties . length ; i ++ ) {
this [ shouldBeReleasedProperties [ i ] ] = null ;
}
if ( process . env . NODE _ENV !== 'production' ) {
Object . defineProperty ( this , 'nativeEvent' , getPooledWarningPropertyDefinition ( 'nativeEvent' , null ) ) ;
Object . defineProperty ( this , 'preventDefault' , getPooledWarningPropertyDefinition ( 'preventDefault' , emptyFunction ) ) ;
Object . defineProperty ( this , 'stopPropagation' , getPooledWarningPropertyDefinition ( 'stopPropagation' , emptyFunction ) ) ;
}
}
} ) ;
SyntheticEvent . Interface = EventInterface ;
if ( process . env . NODE _ENV !== 'production' ) {
if ( isProxySupported ) {
/*eslint-disable no-func-assign */
SyntheticEvent = new Proxy ( SyntheticEvent , {
construct : function ( target , args ) {
return this . apply ( target , Object . create ( target . prototype ) , args ) ;
} ,
apply : function ( constructor , that , args ) {
return new Proxy ( constructor . apply ( that , args ) , {
set : function ( target , prop , value ) {
if ( prop !== 'isPersistent' && ! target . constructor . Interface . hasOwnProperty ( prop ) && shouldBeReleasedProperties . indexOf ( prop ) === - 1 ) {
2017-08-14 05:01:11 +02:00
process . env . NODE _ENV !== 'production' ? warning ( didWarnForAddedNewProperty || target . isPersistent ( ) , "This synthetic event is reused for performance reasons. If you're " + "seeing this, you're adding a new property in the synthetic event object. " + 'The property is never released. See ' + 'https://fb.me/react-event-pooling for more information.' ) : void 0 ;
2017-05-03 15:35:00 +02:00
didWarnForAddedNewProperty = true ;
}
target [ prop ] = value ;
return true ;
}
} ) ;
}
} ) ;
/*eslint-enable no-func-assign */
}
}
/ * *
* Helper to reduce boilerplate when creating subclasses .
*
* @ param { function } Class
* @ param { ? object } Interface
* /
SyntheticEvent . augmentClass = function ( Class , Interface ) {
var Super = this ;
var E = function ( ) { } ;
E . prototype = Super . prototype ;
var prototype = new E ( ) ;
_assign ( prototype , Class . prototype ) ;
Class . prototype = prototype ;
Class . prototype . constructor = Class ;
Class . Interface = _assign ( { } , Super . Interface , Interface ) ;
Class . augmentClass = Super . augmentClass ;
PooledClass . addPoolingTo ( Class , PooledClass . fourArgumentPooler ) ;
} ;
PooledClass . addPoolingTo ( SyntheticEvent , PooledClass . fourArgumentPooler ) ;
module . exports = SyntheticEvent ;
/ * *
* Helper to nullify syntheticEvent instance properties when destructing
*
* @ param { object } SyntheticEvent
* @ param { String } propName
* @ return { object } defineProperty object
* /
function getPooledWarningPropertyDefinition ( propName , getVal ) {
var isFunction = typeof getVal === 'function' ;
return {
configurable : true ,
set : set ,
get : get
} ;
function set ( val ) {
var action = isFunction ? 'setting the method' : 'setting the property' ;
warn ( action , 'This is effectively a no-op' ) ;
return val ;
}
function get ( ) {
var action = isFunction ? 'accessing the method' : 'accessing the property' ;
var result = isFunction ? 'This is a no-op function' : 'This is set to null' ;
warn ( action , result ) ;
return getVal ;
}
function warn ( action , result ) {
var warningCondition = false ;
2017-08-14 05:01:11 +02:00
process . env . NODE _ENV !== 'production' ? warning ( warningCondition , "This synthetic event is reused for performance reasons. If you're seeing this, " + "you're %s `%s` on a released/nullified synthetic event. %s. " + 'If you must keep the original synthetic event around, use event.persist(). ' + 'See https://fb.me/react-event-pooling for more information.' , action , propName , result ) : void 0 ;
2017-05-03 15:35:00 +02:00
}
}