diff --git a/docs/docs/ref-05-events.md b/docs/docs/ref-05-events.md index 8694589dc6..82a2a6113f 100644 --- a/docs/docs/ref-05-events.md +++ b/docs/docs/ref-05-events.md @@ -195,3 +195,11 @@ Number deltaX Number deltaY Number deltaZ ``` + +### Media Events + +Event names: + +``` +onAbort onCanPlay onCanPlayThrough onDurationChange onEmptied onEncrypted onEnded onError onLoadedData onLoadedMetadata onLoadStart onPause onPlay onPlaying onProgress onRateChange onSeeked onSeeking onStalled onSuspend onTimeUpdate onVolumeChange onWaiting +``` diff --git a/src/renderers/dom/client/ReactBrowserEventEmitter.js b/src/renderers/dom/client/ReactBrowserEventEmitter.js index 151435d4fd..abb9f4a6b8 100644 --- a/src/renderers/dom/client/ReactBrowserEventEmitter.js +++ b/src/renderers/dom/client/ReactBrowserEventEmitter.js @@ -84,7 +84,10 @@ var reactTopListenersCounter = 0; // lower node than `document`), binding at `document` would cause duplicate // events so we don't include them here var topEventMapping = { + topAbort: 'abort', topBlur: 'blur', + topCanPlay: 'canplay', + topCanPlayThrough: 'canplaythrough', topChange: 'change', topClick: 'click', topCompositionEnd: 'compositionend', @@ -102,24 +105,44 @@ var topEventMapping = { topDragOver: 'dragover', topDragStart: 'dragstart', topDrop: 'drop', + topDurationChange: 'durationchange', + topEmptied: 'emptied', + topEnded: 'ended', + topError: 'error', topFocus: 'focus', topInput: 'input', topKeyDown: 'keydown', topKeyPress: 'keypress', topKeyUp: 'keyup', + topLoadedData: 'loadeddata', + topLoadedMetadata: 'loadedmetadata', + topLoadStart: 'loadstart', topMouseDown: 'mousedown', topMouseMove: 'mousemove', topMouseOut: 'mouseout', topMouseOver: 'mouseover', topMouseUp: 'mouseup', + topOnEncrypted: 'onencrypted', + topPause: 'pause', topPaste: 'paste', + topPlay: 'play', + topPlaying: 'playing', + topProgress: 'progress', + topRateChange: 'ratechange', + topSeeking: 'seeking', + topSeeked: 'seeked', topScroll: 'scroll', topSelectionChange: 'selectionchange', + topStalled: 'stalled', + topSuspend: 'suspend', topTextInput: 'textInput', + topTimeUpdate: 'timeupdate', topTouchCancel: 'touchcancel', topTouchEnd: 'touchend', topTouchMove: 'touchmove', topTouchStart: 'touchstart', + topVolumeChange: 'volumechange', + topWaiting: 'waiting', topWheel: 'wheel', }; diff --git a/src/renderers/dom/client/eventPlugins/SimpleEventPlugin.js b/src/renderers/dom/client/eventPlugins/SimpleEventPlugin.js index 53d36144f6..f294c9621c 100644 --- a/src/renderers/dom/client/eventPlugins/SimpleEventPlugin.js +++ b/src/renderers/dom/client/eventPlugins/SimpleEventPlugin.js @@ -35,12 +35,30 @@ var warning = require('warning'); var topLevelTypes = EventConstants.topLevelTypes; var eventTypes = { + abort: { + phasedRegistrationNames: { + bubbled: keyOf({onAbort: true}), + captured: keyOf({onAbortCapture: true}), + }, + }, blur: { phasedRegistrationNames: { bubbled: keyOf({onBlur: true}), captured: keyOf({onBlurCapture: true}), }, }, + canPlay: { + phasedRegistrationNames: { + bubbled: keyOf({onCanPlay: true}), + captured: keyOf({onCanPlayCapture: true}), + }, + }, + canPlayThrough: { + phasedRegistrationNames: { + bubbled: keyOf({onCanPlayThrough: true}), + captured: keyOf({onCanPlayThroughCapture: true}), + }, + }, click: { phasedRegistrationNames: { bubbled: keyOf({onClick: true}), @@ -119,6 +137,30 @@ var eventTypes = { captured: keyOf({onDropCapture: true}), }, }, + durationChange: { + phasedRegistrationNames: { + bubbled: keyOf({onDurationChange: true}), + captured: keyOf({onDurationChangeCapture: true}), + }, + }, + emptied: { + phasedRegistrationNames: { + bubbled: keyOf({onEmptied: true}), + captured: keyOf({onEmptiedCapture: true}), + }, + }, + ended: { + phasedRegistrationNames: { + bubbled: keyOf({onEnded: true}), + captured: keyOf({onEndedCapture: true}), + }, + }, + error: { + phasedRegistrationNames: { + bubbled: keyOf({onError: true}), + captured: keyOf({onErrorCapture: true}), + }, + }, focus: { phasedRegistrationNames: { bubbled: keyOf({onFocus: true}), @@ -155,10 +197,22 @@ var eventTypes = { captured: keyOf({onLoadCapture: true}), }, }, - error: { + loadedData: { phasedRegistrationNames: { - bubbled: keyOf({onError: true}), - captured: keyOf({onErrorCapture: true}), + bubbled: keyOf({onLoadedData: true}), + captured: keyOf({onLoadedDataCapture: true}), + }, + }, + loadedMetadata: { + phasedRegistrationNames: { + bubbled: keyOf({onLoadedMetadata: true}), + captured: keyOf({onLoadedMetadataCapture: true}), + }, + }, + loadStart: { + phasedRegistrationNames: { + bubbled: keyOf({onLoadStart: true}), + captured: keyOf({onLoadStartCapture: true}), }, }, // Note: We do not allow listening to mouseOver events. Instead, use the @@ -193,12 +247,48 @@ var eventTypes = { captured: keyOf({onMouseUpCapture: true}), }, }, + onEncrypted: { + phasedRegistrationNames: { + bubbled: keyOf({onEncrypted: true}), + captured: keyOf({onEncryptedCapture: true}), + }, + }, paste: { phasedRegistrationNames: { bubbled: keyOf({onPaste: true}), captured: keyOf({onPasteCapture: true}), }, }, + pause: { + phasedRegistrationNames: { + bubbled: keyOf({onPause: true}), + captured: keyOf({onPauseCapture: true}), + }, + }, + play: { + phasedRegistrationNames: { + bubbled: keyOf({onPlay: true}), + captured: keyOf({onPlayCapture: true}), + }, + }, + playing: { + phasedRegistrationNames: { + bubbled: keyOf({onPlaying: true}), + captured: keyOf({onPlayingCapture: true}), + }, + }, + progress: { + phasedRegistrationNames: { + bubbled: keyOf({onProgress: true}), + captured: keyOf({onProgressCapture: true}), + }, + }, + rateChange: { + phasedRegistrationNames: { + bubbled: keyOf({onRateChange: true}), + captured: keyOf({onRateChangeCapture: true}), + }, + }, reset: { phasedRegistrationNames: { bubbled: keyOf({onReset: true}), @@ -211,12 +301,42 @@ var eventTypes = { captured: keyOf({onScrollCapture: true}), }, }, + seeked: { + phasedRegistrationNames: { + bubbled: keyOf({onSeeked: true}), + captured: keyOf({onSeekedCapture: true}), + }, + }, + seeking: { + phasedRegistrationNames: { + bubbled: keyOf({onSeeking: true}), + captured: keyOf({onSeekingCapture: true}), + }, + }, + stalled: { + phasedRegistrationNames: { + bubbled: keyOf({onStalled: true}), + captured: keyOf({onStalledCapture: true}), + }, + }, submit: { phasedRegistrationNames: { bubbled: keyOf({onSubmit: true}), captured: keyOf({onSubmitCapture: true}), }, }, + suspend: { + phasedRegistrationNames: { + bubbled: keyOf({onSuspend: true}), + captured: keyOf({onSuspendCapture: true}), + }, + }, + timeUpdate: { + phasedRegistrationNames: { + bubbled: keyOf({onTimeUpdate: true}), + captured: keyOf({onTimeUpdateCapture: true}), + }, + }, touchCancel: { phasedRegistrationNames: { bubbled: keyOf({onTouchCancel: true}), @@ -241,6 +361,18 @@ var eventTypes = { captured: keyOf({onTouchStartCapture: true}), }, }, + volumeChange: { + phasedRegistrationNames: { + bubbled: keyOf({onVolumeChange: true}), + captured: keyOf({onVolumeChangeCapture: true}), + }, + }, + waiting: { + phasedRegistrationNames: { + bubbled: keyOf({onWaiting: true}), + captured: keyOf({onWaitingCapture: true}), + }, + }, wheel: { phasedRegistrationNames: { bubbled: keyOf({onWheel: true}), @@ -250,41 +382,63 @@ var eventTypes = { }; var topLevelEventsToDispatchConfig = { - topBlur: eventTypes.blur, - topClick: eventTypes.click, - topContextMenu: eventTypes.contextMenu, - topCopy: eventTypes.copy, - topCut: eventTypes.cut, - topDoubleClick: eventTypes.doubleClick, - topDrag: eventTypes.drag, - topDragEnd: eventTypes.dragEnd, - topDragEnter: eventTypes.dragEnter, - topDragExit: eventTypes.dragExit, - topDragLeave: eventTypes.dragLeave, - topDragOver: eventTypes.dragOver, - topDragStart: eventTypes.dragStart, - topDrop: eventTypes.drop, - topError: eventTypes.error, - topFocus: eventTypes.focus, - topInput: eventTypes.input, - topKeyDown: eventTypes.keyDown, - topKeyPress: eventTypes.keyPress, - topKeyUp: eventTypes.keyUp, - topLoad: eventTypes.load, - topMouseDown: eventTypes.mouseDown, - topMouseMove: eventTypes.mouseMove, - topMouseOut: eventTypes.mouseOut, - topMouseOver: eventTypes.mouseOver, - topMouseUp: eventTypes.mouseUp, - topPaste: eventTypes.paste, - topReset: eventTypes.reset, - topScroll: eventTypes.scroll, - topSubmit: eventTypes.submit, - topTouchCancel: eventTypes.touchCancel, - topTouchEnd: eventTypes.touchEnd, - topTouchMove: eventTypes.touchMove, - topTouchStart: eventTypes.touchStart, - topWheel: eventTypes.wheel, + topAbort: eventTypes.abort, + topBlur: eventTypes.blur, + topCanPlay: eventTypes.canPlay, + topCanPlayThrough: eventTypes.canPlayThrough, + topClick: eventTypes.click, + topContextMenu: eventTypes.contextMenu, + topCopy: eventTypes.copy, + topCut: eventTypes.cut, + topDoubleClick: eventTypes.doubleClick, + topDrag: eventTypes.drag, + topDragEnd: eventTypes.dragEnd, + topDragEnter: eventTypes.dragEnter, + topDragExit: eventTypes.dragExit, + topDragLeave: eventTypes.dragLeave, + topDragOver: eventTypes.dragOver, + topDragStart: eventTypes.dragStart, + topDrop: eventTypes.drop, + topDurationChange: eventTypes.durationChange, + topEmptied: eventTypes.emptied, + topEnded: eventTypes.ended, + topError: eventTypes.error, + topFocus: eventTypes.focus, + topInput: eventTypes.input, + topKeyDown: eventTypes.keyDown, + topKeyPress: eventTypes.keyPress, + topKeyUp: eventTypes.keyUp, + topLoad: eventTypes.load, + topLoadedData: eventTypes.loadedData, + topLoadedMetadata: eventTypes.loadedMetadata, + topLoadStart: eventTypes.loadStart, + topMouseDown: eventTypes.mouseDown, + topMouseMove: eventTypes.mouseMove, + topMouseOut: eventTypes.mouseOut, + topMouseOver: eventTypes.mouseOver, + topMouseUp: eventTypes.mouseUp, + topOnEncrypted: eventTypes.onEncrypted, + topPause: eventTypes.pause, + topPaste: eventTypes.paste, + topPlay: eventTypes.play, + topPlaying: eventTypes.playing, + topProgress: eventTypes.progress, + topRateChange: eventTypes.rateChange, + topReset: eventTypes.reset, + topSeeked: eventTypes.seeked, + topSeeking: eventTypes.seeking, + topScroll: eventTypes.scroll, + topStalled: eventTypes.stalled, + topSubmit: eventTypes.submit, + topSuspend: eventTypes.suspend, + topTimeUpdate: eventTypes.timeUpdate, + topTouchCancel: eventTypes.touchCancel, + topTouchEnd: eventTypes.touchEnd, + topTouchMove: eventTypes.touchMove, + topTouchStart: eventTypes.touchStart, + topVolumeChange: eventTypes.volumeChange, + topWaiting: eventTypes.waiting, + topWheel: eventTypes.wheel, }; for (var type in topLevelEventsToDispatchConfig) { @@ -342,11 +496,33 @@ var SimpleEventPlugin = { } var EventConstructor; switch (topLevelType) { + case topLevelTypes.topAbort: + case topLevelTypes.topCanPlay: + case topLevelTypes.topCanPlayThrough: + case topLevelTypes.topDurationChange: + case topLevelTypes.topEmptied: + case topLevelTypes.topEnded: + case topLevelTypes.topError: case topLevelTypes.topInput: case topLevelTypes.topLoad: - case topLevelTypes.topError: + case topLevelTypes.topLoadedData: + case topLevelTypes.topLoadedMetadata: + case topLevelTypes.topLoadStart: + case topLevelTypes.topOnEncrypted: + case topLevelTypes.topPause: + case topLevelTypes.topPlay: + case topLevelTypes.topPlaying: + case topLevelTypes.topProgress: + case topLevelTypes.topRateChange: case topLevelTypes.topReset: + case topLevelTypes.topSeeked: + case topLevelTypes.topSeeking: + case topLevelTypes.topStalled: case topLevelTypes.topSubmit: + case topLevelTypes.topSuspend: + case topLevelTypes.topTimeUpdate: + case topLevelTypes.topVolumeChange: + case topLevelTypes.topWaiting: // HTML Events // @see http://www.w3.org/TR/html5/index.html#events-0 EventConstructor = SyntheticEvent; diff --git a/src/renderers/dom/shared/ReactDOMComponent.js b/src/renderers/dom/shared/ReactDOMComponent.js index 4ed845c5b8..f9e5601ca2 100644 --- a/src/renderers/dom/shared/ReactDOMComponent.js +++ b/src/renderers/dom/shared/ReactDOMComponent.js @@ -301,6 +301,34 @@ function putListener() { ); } +// There are so many media events, it makes sense to just +// maintain a list rather than create a `trapBubbledEvent` for each +var mediaEvents = { + topAbort: 'abort', + topCanPlay: 'canplay', + topCanPlayThrough: 'canplaythrough', + topDurationChange: 'durationchange', + topEmptied: 'emptied', + topEnded: 'ended', + topError: 'error', + topLoadedData: 'loadeddata', + topLoadedMetadata: 'loadedmetadata', + topLoadStart: 'loadstart', + topOnEncrypted: 'onencrypted', + topPause: 'pause', + topPlay: 'play', + topPlaying: 'playing', + topProgress: 'progress', + topRateChange: 'ratechange', + topSeeked: 'seeked', + topSeeking: 'seeking', + topStalled: 'stalled', + topSuspend: 'suspend', + topTimeUpdate: 'timeupdate', + topVolumeChange: 'volumechange', + topWaiting: 'waiting', +}; + function trapBubbledEventsLocal() { var inst = this; // If a component renders to null or if another component fatals and causes @@ -321,6 +349,24 @@ function trapBubbledEventsLocal() { node ), ]; + break; + case 'video': + case 'audio': + + inst._wrapperState.listeners = []; + // create listener for each media event + for (var event in mediaEvents) { + if (mediaEvents.hasOwnProperty(event)) { + inst._wrapperState.listeners.push( + ReactBrowserEventEmitter.trapBubbledEvent( + EventConstants.topLevelTypes[event], + mediaEvents[event], + node + ) + ); + } + } + break; case 'img': inst._wrapperState.listeners = [ @@ -475,6 +521,8 @@ ReactDOMComponent.Mixin = { case 'iframe': case 'img': case 'form': + case 'video': + case 'audio': this._wrapperState = { listeners: null, }; @@ -902,6 +950,8 @@ ReactDOMComponent.Mixin = { case 'iframe': case 'img': case 'form': + case 'video': + case 'audio': var listeners = this._wrapperState.listeners; if (listeners) { for (var i = 0; i < listeners.length; i++) { diff --git a/src/renderers/shared/event/EventConstants.js b/src/renderers/shared/event/EventConstants.js index c02918ed1a..5e8ccbc43d 100644 --- a/src/renderers/shared/event/EventConstants.js +++ b/src/renderers/shared/event/EventConstants.js @@ -19,7 +19,10 @@ var PropagationPhases = keyMirror({bubbled: null, captured: null}); * Types of raw signals from the browser caught at the top level. */ var topLevelTypes = keyMirror({ + topAbort: null, topBlur: null, + topCanPlay: null, + topCanPlayThrough: null, topChange: null, topClick: null, topCompositionEnd: null, @@ -37,6 +40,9 @@ var topLevelTypes = keyMirror({ topDragOver: null, topDragStart: null, topDrop: null, + topDurationChange: null, + topEmptied: null, + topEnded: null, topError: null, topFocus: null, topInput: null, @@ -44,21 +50,37 @@ var topLevelTypes = keyMirror({ topKeyPress: null, topKeyUp: null, topLoad: null, + topLoadedData: null, + topLoadedMetadata: null, + topLoadStart: null, topMouseDown: null, topMouseMove: null, topMouseOut: null, topMouseOver: null, topMouseUp: null, + topOnEncrypted: null, topPaste: null, + topPause: null, + topPlay: null, + topPlaying: null, + topProgress: null, + topRateChange: null, topReset: null, topScroll: null, + topSeeked: null, + topSeeking: null, topSelectionChange: null, + topStalled: null, + topSuspend: null, topSubmit: null, topTextInput: null, + topTimeUpdate: null, topTouchCancel: null, topTouchEnd: null, topTouchMove: null, topTouchStart: null, + topVolumeChange: null, + topWaiting: null, topWheel: null, });