react / react-0.13.3 / examples / basic-commonjs / node_modules / reactify / node_modules / react-tools / src / browser / eventPlugins / CompositionEventPlugin.js
81155 views/**1* Copyright 2013-2014, Facebook, Inc.2* All rights reserved.3*4* This source code is licensed under the BSD-style license found in the5* LICENSE file in the root directory of this source tree. An additional grant6* of patent rights can be found in the PATENTS file in the same directory.7*8* @providesModule CompositionEventPlugin9* @typechecks static-only10*/1112"use strict";1314var EventConstants = require('EventConstants');15var EventPropagators = require('EventPropagators');16var ExecutionEnvironment = require('ExecutionEnvironment');17var ReactInputSelection = require('ReactInputSelection');18var SyntheticCompositionEvent = require('SyntheticCompositionEvent');1920var getTextContentAccessor = require('getTextContentAccessor');21var keyOf = require('keyOf');2223var END_KEYCODES = [9, 13, 27, 32]; // Tab, Return, Esc, Space24var START_KEYCODE = 229;2526var useCompositionEvent = (27ExecutionEnvironment.canUseDOM &&28'CompositionEvent' in window29);3031// In IE9+, we have access to composition events, but the data supplied32// by the native compositionend event may be incorrect. In Korean, for example,33// the compositionend event contains only one character regardless of34// how many characters have been composed since compositionstart.35// We therefore use the fallback data while still using the native36// events as triggers.37var useFallbackData = (38!useCompositionEvent ||39(40'documentMode' in document &&41document.documentMode > 8 &&42document.documentMode <= 1143)44);4546var topLevelTypes = EventConstants.topLevelTypes;47var currentComposition = null;4849// Events and their corresponding property names.50var eventTypes = {51compositionEnd: {52phasedRegistrationNames: {53bubbled: keyOf({onCompositionEnd: null}),54captured: keyOf({onCompositionEndCapture: null})55},56dependencies: [57topLevelTypes.topBlur,58topLevelTypes.topCompositionEnd,59topLevelTypes.topKeyDown,60topLevelTypes.topKeyPress,61topLevelTypes.topKeyUp,62topLevelTypes.topMouseDown63]64},65compositionStart: {66phasedRegistrationNames: {67bubbled: keyOf({onCompositionStart: null}),68captured: keyOf({onCompositionStartCapture: null})69},70dependencies: [71topLevelTypes.topBlur,72topLevelTypes.topCompositionStart,73topLevelTypes.topKeyDown,74topLevelTypes.topKeyPress,75topLevelTypes.topKeyUp,76topLevelTypes.topMouseDown77]78},79compositionUpdate: {80phasedRegistrationNames: {81bubbled: keyOf({onCompositionUpdate: null}),82captured: keyOf({onCompositionUpdateCapture: null})83},84dependencies: [85topLevelTypes.topBlur,86topLevelTypes.topCompositionUpdate,87topLevelTypes.topKeyDown,88topLevelTypes.topKeyPress,89topLevelTypes.topKeyUp,90topLevelTypes.topMouseDown91]92}93};9495/**96* Translate native top level events into event types.97*98* @param {string} topLevelType99* @return {object}100*/101function getCompositionEventType(topLevelType) {102switch (topLevelType) {103case topLevelTypes.topCompositionStart:104return eventTypes.compositionStart;105case topLevelTypes.topCompositionEnd:106return eventTypes.compositionEnd;107case topLevelTypes.topCompositionUpdate:108return eventTypes.compositionUpdate;109}110}111112/**113* Does our fallback best-guess model think this event signifies that114* composition has begun?115*116* @param {string} topLevelType117* @param {object} nativeEvent118* @return {boolean}119*/120function isFallbackStart(topLevelType, nativeEvent) {121return (122topLevelType === topLevelTypes.topKeyDown &&123nativeEvent.keyCode === START_KEYCODE124);125}126127/**128* Does our fallback mode think that this event is the end of composition?129*130* @param {string} topLevelType131* @param {object} nativeEvent132* @return {boolean}133*/134function isFallbackEnd(topLevelType, nativeEvent) {135switch (topLevelType) {136case topLevelTypes.topKeyUp:137// Command keys insert or clear IME input.138return (END_KEYCODES.indexOf(nativeEvent.keyCode) !== -1);139case topLevelTypes.topKeyDown:140// Expect IME keyCode on each keydown. If we get any other141// code we must have exited earlier.142return (nativeEvent.keyCode !== START_KEYCODE);143case topLevelTypes.topKeyPress:144case topLevelTypes.topMouseDown:145case topLevelTypes.topBlur:146// Events are not possible without cancelling IME.147return true;148default:149return false;150}151}152153/**154* Helper class stores information about selection and document state155* so we can figure out what changed at a later date.156*157* @param {DOMEventTarget} root158*/159function FallbackCompositionState(root) {160this.root = root;161this.startSelection = ReactInputSelection.getSelection(root);162this.startValue = this.getText();163}164165/**166* Get current text of input.167*168* @return {string}169*/170FallbackCompositionState.prototype.getText = function() {171return this.root.value || this.root[getTextContentAccessor()];172};173174/**175* Text that has changed since the start of composition.176*177* @return {string}178*/179FallbackCompositionState.prototype.getData = function() {180var endValue = this.getText();181var prefixLength = this.startSelection.start;182var suffixLength = this.startValue.length - this.startSelection.end;183184return endValue.substr(185prefixLength,186endValue.length - suffixLength - prefixLength187);188};189190/**191* This plugin creates `onCompositionStart`, `onCompositionUpdate` and192* `onCompositionEnd` events on inputs, textareas and contentEditable193* nodes.194*/195var CompositionEventPlugin = {196197eventTypes: eventTypes,198199/**200* @param {string} topLevelType Record from `EventConstants`.201* @param {DOMEventTarget} topLevelTarget The listening component root node.202* @param {string} topLevelTargetID ID of `topLevelTarget`.203* @param {object} nativeEvent Native browser event.204* @return {*} An accumulation of synthetic events.205* @see {EventPluginHub.extractEvents}206*/207extractEvents: function(208topLevelType,209topLevelTarget,210topLevelTargetID,211nativeEvent) {212213var eventType;214var data;215216if (useCompositionEvent) {217eventType = getCompositionEventType(topLevelType);218} else if (!currentComposition) {219if (isFallbackStart(topLevelType, nativeEvent)) {220eventType = eventTypes.compositionStart;221}222} else if (isFallbackEnd(topLevelType, nativeEvent)) {223eventType = eventTypes.compositionEnd;224}225226if (useFallbackData) {227// The current composition is stored statically and must not be228// overwritten while composition continues.229if (!currentComposition && eventType === eventTypes.compositionStart) {230currentComposition = new FallbackCompositionState(topLevelTarget);231} else if (eventType === eventTypes.compositionEnd) {232if (currentComposition) {233data = currentComposition.getData();234currentComposition = null;235}236}237}238239if (eventType) {240var event = SyntheticCompositionEvent.getPooled(241eventType,242topLevelTargetID,243nativeEvent244);245if (data) {246// Inject data generated from fallback path into the synthetic event.247// This matches the property of native CompositionEventInterface.248event.data = data;249}250EventPropagators.accumulateTwoPhaseDispatches(event);251return event;252}253}254};255256module.exports = CompositionEventPlugin;257258259